From 31af0ed4c6e7acb0eb6fd838c4289e8a594224e3 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Sun, 8 Feb 2015 04:52:01 -0800 Subject: [PATCH] Import of Rel-2-BETA-WW05-15 --- Makefile | 182 + arduino/clloader/clloader.c | 11 +- arduino/clloader/clloader.h | 1 + .../BCM43341B0_002.001.014.0123.0168.hcd | Bin 0 -> 41773 bytes broadcom_cws/wlan/driver_bcm43x/wl_cfg80211.c | 55 +- .../bcm43340-tools/dhdutil.bb | 15 + .../bcm43340-tools/wlx.bb | 19 + .../recipes-connectivity/wfa-tool/wfa-tool.bb | 3 + .../conf/distro/poky-edison.conf | 4 +- .../files/bcm43341.conf | 4 +- .../files/bluetooth_rfkill_event.c | 17 +- .../bluez5/bluez5_5.15.bbappend | 24 +- .../bluez5/files/bluetooth.conf | 8 + .../files/obex_set_dbus_session_service.patch | 10 + .../connman/connman/disable_p2p.patch | 15 + .../connman/connman_1.27.bb | 11 + .../connman/connman_1.27.bbappend | 35 + .../hostapd/files/hostapd.conf-sane | 2 +- .../hostapd/files/udhcpd-for-hostapd.conf | 2 +- .../openssh/openssh/sshdgenkeys.service | 2 +- .../openssl-1.0.1e-cve-2014-0195.patch | 40 + .../openssl-1.0.1e-cve-2014-0198.patch | 38 + .../openssl-1.0.1e-cve-2014-0221.patch | 38 + .../openssl-1.0.1e-cve-2014-0224.patch | 103 + .../openssl-1.0.1e-cve-2014-3470.patch | 31 + .../openssl/openssl_1.0.1j.bb | 54 + .../wpa-supplicant/defconfig-gnutls | 11 +- .../wpa-supplicant/p2p_supplicant.conf-sane | 12 + .../wpa-supplicant/udhcpd-p2p.conf | 107 + .../wpa-supplicant-android-4.4.4_r2.0.1.patch | 184 + .../wpa-supplicant/wpa_cli-actions.sh | 45 +- .../wpa-supplicant/wpa_supplicant.conf-sane | 7 +- .../wpa-supplicant/wpa_supplicant.service | 10 +- .../wpa_supplicant_p2p_event.service | 13 + .../wpa_supplicant_wlan0_event.service | 13 + .../wpa-supplicant_2.1.bbappend | 33 +- .../base-files/media-sdcard.automount | 11 + .../base-files/base-files/media-sdcard.mount | 7 +- .../base-files/base-files/share/dot.profile | 9 + .../base-files/base-files_3.0.14.bbappend | 13 +- .../busybox/busybox_1.22.1.bbappend | 7 +- .../busybox/files/busybox-log.cfg | 3 + .../first-install/files/first-install.service | 2 +- .../first-install/files/first-install.sh | 18 +- .../recipes-core/images/edison-image.bb | 30 +- .../ota-update/files/ota-update.sh | 4 +- ...segfault-when-pressing-DEL-key-twice.patch | 39 + .../readline/readline_6.3.bbappend | 4 + .../resize-rootfs/files/resize-rootfs.service | 14 + .../resize-rootfs/resize-rootfs.bb | 24 + .../systemd/files/edison-machine-id.service | 14 + .../systemd/files/hsu-pm-runtime.service | 1 + .../recipes-core/systemd/files/usb0.network | 6 + .../recipes-core/systemd/systemd_%.bbappend | 16 +- .../e2fsprogs/e2fsprogs_%.bbappend | 11 + .../recipes-devtools/swig/swig.inc | 63 + ...e-for-swig-swiglib-on-non-Win32-plat.patch | 56 + ...re-use-pkg-config-for-pcre-detection.patch | 55 + .../recipes-devtools/swig/swig_3.0.2.bb | 8 + .../alsa/alsa-utils_%.bbappend | 13 + .../alsa/files/asound.state | 14141 +++++++ .../libav/libav_0.8.9.bbappend | 23 + .../mplayer/mplayer-common.bb | 22 + .../mplayer/mplayer-common/mplayer.conf | 15 + .../mplayer2/cross.compile.codec-cfg.patch | 16 + .../mplayer/mplayer2_git.bb | 161 + .../pulseaudio/files/pulseaudio.service | 12 + .../pulseaudio/files/system.pa | 66 + .../pulseaudio/pulseaudio-service_1.0.bb | 28 + .../pulseaudio/pulseaudio_5.0.bbappend | 22 + .../blink-led/blink-led_0.1.bb | 23 + .../recipes-support/blink-led/files/blink-led | 109 + .../blink-led/files/blink-led.service | 10 + .../cleanjournal/cleanjournal.bb | 31 + .../cleanjournal/files/clean_journal.sh | 60 + .../cleanjournal/files/cleanjournal.service | 10 + .../crashlog/files/retrieve_crashlog.sh | 73 +- .../edison-mcu/files/intel_mcu.bin | Bin 0 -> 12212 bytes .../edison-mcu/files/mcu_fw_loader.service | 10 + .../edison-mcu/files/mcu_fw_loader.sh | 12 + .../edison-mcu/mcu-fw-bin_0.1.bb | 17 + .../edison-mcu/mcu-fw-load_0.1.bb | 23 + .../edison-sst/files/fw_sst_119a.bin | Bin 0 -> 464927 bytes .../edison-sst/sst-fw-bin_0.1.bb | 19 + .../files/pwr-button-handler.c | 41 +- .../files/pwr-button-handler.service | 2 +- .../c-ares/c-ares_1.10.0.bb | 13 + .../mdns/files/build.patch | 191 + .../mdns/files/mdns.service | 4 + .../recipes-connectivity/mdns/mdns_544.bb | 23 +- .../mosquitto/files/build.patch | 71 + .../mosquitto/files/mosquitto.service | 15 + .../mosquitto/mosquitto_1.3.4.bb | 55 + .../paho-mqtt/files/makefile.patch | 19 + .../paho-mqtt/paho-mqtt_3.1.bb | 21 +- .../recipes-connectivity/sshpass/sshpass.inc | 10 + .../sshpass/sshpass_1.05.bb | 8 + .../recipes-connectivity/zeromq/cppzmq_git.bb | 19 + .../zeromq/zeromq_4.0.4.bb | 6 +- .../recipes-core/images/edison-image.bbappend | 25 +- .../iotkit-agent/iotkit-agent_0.2.0.bb | 84 + .../iotkit-comm-c/iotkit-comm-c_0.1.1.bb | 17 +- .../iotkit-comm-js/iotkit-comm-js_0.1.1.bb | 57 +- .../iotkit-lib-c/iotkit-lib-c_0.1.0.bb | 49 + .../iotkit-opkg/files/iotkit.conf | 1 + .../iotkit-opkg/iotkit-opkg_0.0.1.bb | 22 + .../recipes-devtools/mraa/mraa_0.5.2.bb | 27 + .../recipes-devtools/nodejs/nodejs_0.10.28.bb | 26 +- .../recipes-devtools/oobe/oobe_0.0.1.bb | 31 +- .../recipes-devtools/upm/upm_0.1.8.bb | 17 + .../xdk-daemon/xdk-daemon_0.0.27.bb | 68 + .../meta-edison/conf/machine/edison.conf | 2 +- .../recipes-bsp/u-boot/files/edison.env | 56 + .../recipes-bsp/u-boot/files/fw_env.config | 11 + .../u-boot/files/target_env/blankcdc.env | 10 + .../u-boot/files/target_env/blankrndis.env | 10 + .../u-boot/files/target_env/defaultcdc.env | 10 + .../u-boot/files/target_env/defaultrndis.env | 10 + .../u-boot/files/target_env/ifwi.env | 7 + .../u-boot/files/target_env/prod.env | 6 + .../u-boot/files/upstream_to_edison.patch | 11157 ++++++ .../u-boot/u-boot-fw-utils_2014.04.bb | 28 + .../recipes-bsp/u-boot/u-boot-internal.inc | 10 + .../recipes-bsp/u-boot/u-boot-osip.inc | 208 + .../recipes-bsp/u-boot/u-boot-target-env.inc | 93 + .../u-boot/u-boot-tools_2014.04.bb | 22 + .../recipes-bsp/u-boot/u-boot_2014.04.bb | 7 + .../recipes-kernel/linux/files/defconfig | 106 +- .../linux/files/upstream_to_edison.patch | 30701 +++++++++++++++- .../linux/linux-yocto_3.10.bbappend | 10 - device-software/setup.sh | 64 +- ...handle-syslog-related-files-properly.patch | 74 + ...ternal-src-builds-when-S-B-poky-dora.patch | 3 +- ...rchive-avoid-dependency-on-e2fsprogs.patch | 55 + ...reen-sessions-being-killed-on-discon.patch | 36 + device-software/utils/Makefile.mk | 182 + device-software/utils/create-debian-image.sh | 226 + .../utils/create_devtools_package.sh | 25 +- device-software/utils/flash/flashall.bat | 63 +- device-software/utils/flash/flashall.sh | 85 +- .../flash/ifwi/edison/edison_dnx_fwr.bin | Bin 98196 -> 98196 bytes .../flash/ifwi/edison/edison_dnx_osr.bin | Bin 148996 -> 148996 bytes .../flash/ifwi/edison/edison_ifwi-dbg-00.bin | Bin 4194468 -> 4194468 bytes .../flash/ifwi/edison/edison_ifwi-dbg-01.bin | Bin 4194468 -> 4194468 bytes .../flash/ifwi/edison/edison_ifwi-dbg-02.bin | Bin 4194468 -> 4194468 bytes .../flash/ifwi/edison/edison_ifwi-dbg-03.bin | Bin 4194468 -> 4194468 bytes .../flash/ifwi/edison/edison_ifwi-dbg-04.bin | Bin 4194468 -> 4194468 bytes .../flash/ifwi/edison/edison_ifwi-dbg-05.bin | Bin 4194468 -> 4194468 bytes .../flash/ifwi/edison/edison_ifwi-dbg-06.bin | Bin 4194468 -> 4194468 bytes device-software/utils/flash/postBuild.sh | 74 +- device-software/utils/handle_bash_func.patch | 41 + device-software/utils/invalidate_sstate.sh | 19 +- .../sdk-populate-clean-broken-links.patch | 6 +- mw/oobe/LICENSE | 196 +- mw/oobe/package.json | 11 + mw/oobe/src/configure_edison | 146 +- mw/oobe/src/public/exit-upgrade.html | 33 + mw/oobe/src/public/exit.html | 12 +- mw/oobe/src/public/exiting-without-wifi.html | 12 +- mw/oobe/src/public/index.html | 168 +- mw/oobe/src/public/main.css | 30 +- mw/oobe/src/public/upgrade.html | 61 + mw/oobe/src/server.js | 75 +- 163 files changed, 60762 insertions(+), 811 deletions(-) create mode 100644 Makefile create mode 100644 broadcom_cws/bluetooth/firmware/BCM43341B0_002.001.014.0123.0168.hcd create mode 100644 device-software/meta-edison-devtools/recipes-connectivity/bcm43340-tools/dhdutil.bb create mode 100644 device-software/meta-edison-devtools/recipes-connectivity/bcm43340-tools/wlx.bb create mode 100644 device-software/meta-edison-distro/recipes-connectivity/bluez5/files/obex_set_dbus_session_service.patch create mode 100644 device-software/meta-edison-distro/recipes-connectivity/connman/connman/disable_p2p.patch create mode 100644 device-software/meta-edison-distro/recipes-connectivity/connman/connman_1.27.bb create mode 100644 device-software/meta-edison-distro/recipes-connectivity/connman/connman_1.27.bbappend create mode 100644 device-software/meta-edison-distro/recipes-connectivity/openssl/openssl/openssl-1.0.1e-cve-2014-0195.patch create mode 100644 device-software/meta-edison-distro/recipes-connectivity/openssl/openssl/openssl-1.0.1e-cve-2014-0198.patch create mode 100644 device-software/meta-edison-distro/recipes-connectivity/openssl/openssl/openssl-1.0.1e-cve-2014-0221.patch create mode 100644 device-software/meta-edison-distro/recipes-connectivity/openssl/openssl/openssl-1.0.1e-cve-2014-0224.patch create mode 100644 device-software/meta-edison-distro/recipes-connectivity/openssl/openssl/openssl-1.0.1e-cve-2014-3470.patch create mode 100644 device-software/meta-edison-distro/recipes-connectivity/openssl/openssl_1.0.1j.bb create mode 100644 device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/p2p_supplicant.conf-sane create mode 100644 device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/udhcpd-p2p.conf create mode 100644 device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/wpa-supplicant-android-4.4.4_r2.0.1.patch create mode 100644 device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/wpa_supplicant_p2p_event.service create mode 100644 device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/wpa_supplicant_wlan0_event.service create mode 100644 device-software/meta-edison-distro/recipes-core/base-files/base-files/media-sdcard.automount create mode 100644 device-software/meta-edison-distro/recipes-core/base-files/base-files/share/dot.profile create mode 100644 device-software/meta-edison-distro/recipes-core/busybox/files/busybox-log.cfg create mode 100644 device-software/meta-edison-distro/recipes-core/readline/files/readline-fix-segfault-when-pressing-DEL-key-twice.patch create mode 100644 device-software/meta-edison-distro/recipes-core/readline/readline_6.3.bbappend create mode 100644 device-software/meta-edison-distro/recipes-core/resize-rootfs/files/resize-rootfs.service create mode 100644 device-software/meta-edison-distro/recipes-core/resize-rootfs/resize-rootfs.bb create mode 100644 device-software/meta-edison-distro/recipes-core/systemd/files/edison-machine-id.service create mode 100644 device-software/meta-edison-distro/recipes-core/systemd/files/usb0.network create mode 100644 device-software/meta-edison-distro/recipes-devtools/e2fsprogs/e2fsprogs_%.bbappend create mode 100644 device-software/meta-edison-distro/recipes-devtools/swig/swig.inc create mode 100644 device-software/meta-edison-distro/recipes-devtools/swig/swig/0001-Use-proc-self-exe-for-swig-swiglib-on-non-Win32-plat.patch create mode 100644 device-software/meta-edison-distro/recipes-devtools/swig/swig/0001-configure-use-pkg-config-for-pcre-detection.patch create mode 100644 device-software/meta-edison-distro/recipes-devtools/swig/swig_3.0.2.bb create mode 100644 device-software/meta-edison-distro/recipes-multimedia/alsa/alsa-utils_%.bbappend create mode 100644 device-software/meta-edison-distro/recipes-multimedia/alsa/files/asound.state create mode 100644 device-software/meta-edison-distro/recipes-multimedia/libav/libav_0.8.9.bbappend create mode 100644 device-software/meta-edison-distro/recipes-multimedia/mplayer/mplayer-common.bb create mode 100644 device-software/meta-edison-distro/recipes-multimedia/mplayer/mplayer-common/mplayer.conf create mode 100644 device-software/meta-edison-distro/recipes-multimedia/mplayer/mplayer2/cross.compile.codec-cfg.patch create mode 100644 device-software/meta-edison-distro/recipes-multimedia/mplayer/mplayer2_git.bb create mode 100644 device-software/meta-edison-distro/recipes-multimedia/pulseaudio/files/pulseaudio.service create mode 100644 device-software/meta-edison-distro/recipes-multimedia/pulseaudio/files/system.pa create mode 100644 device-software/meta-edison-distro/recipes-multimedia/pulseaudio/pulseaudio-service_1.0.bb create mode 100644 device-software/meta-edison-distro/recipes-multimedia/pulseaudio/pulseaudio_5.0.bbappend create mode 100644 device-software/meta-edison-distro/recipes-support/blink-led/blink-led_0.1.bb create mode 100755 device-software/meta-edison-distro/recipes-support/blink-led/files/blink-led create mode 100644 device-software/meta-edison-distro/recipes-support/blink-led/files/blink-led.service create mode 100644 device-software/meta-edison-distro/recipes-support/cleanjournal/cleanjournal.bb create mode 100644 device-software/meta-edison-distro/recipes-support/cleanjournal/files/clean_journal.sh create mode 100644 device-software/meta-edison-distro/recipes-support/cleanjournal/files/cleanjournal.service create mode 100644 device-software/meta-edison-distro/recipes-support/edison-mcu/files/intel_mcu.bin create mode 100644 device-software/meta-edison-distro/recipes-support/edison-mcu/files/mcu_fw_loader.service create mode 100644 device-software/meta-edison-distro/recipes-support/edison-mcu/files/mcu_fw_loader.sh create mode 100644 device-software/meta-edison-distro/recipes-support/edison-mcu/mcu-fw-bin_0.1.bb create mode 100644 device-software/meta-edison-distro/recipes-support/edison-mcu/mcu-fw-load_0.1.bb create mode 100644 device-software/meta-edison-distro/recipes-support/edison-sst/files/fw_sst_119a.bin create mode 100644 device-software/meta-edison-distro/recipes-support/edison-sst/sst-fw-bin_0.1.bb create mode 100644 device-software/meta-edison-middleware/recipes-connectivity/c-ares/c-ares_1.10.0.bb create mode 100644 device-software/meta-edison-middleware/recipes-connectivity/mdns/files/build.patch create mode 100644 device-software/meta-edison-middleware/recipes-connectivity/mosquitto/files/build.patch create mode 100644 device-software/meta-edison-middleware/recipes-connectivity/mosquitto/files/mosquitto.service create mode 100644 device-software/meta-edison-middleware/recipes-connectivity/mosquitto/mosquitto_1.3.4.bb create mode 100644 device-software/meta-edison-middleware/recipes-connectivity/paho-mqtt/files/makefile.patch create mode 100644 device-software/meta-edison-middleware/recipes-connectivity/sshpass/sshpass.inc create mode 100644 device-software/meta-edison-middleware/recipes-connectivity/sshpass/sshpass_1.05.bb create mode 100644 device-software/meta-edison-middleware/recipes-connectivity/zeromq/cppzmq_git.bb create mode 100644 device-software/meta-edison-middleware/recipes-devtools/iotkit-agent/iotkit-agent_0.2.0.bb create mode 100644 device-software/meta-edison-middleware/recipes-devtools/iotkit-lib-c/iotkit-lib-c_0.1.0.bb create mode 100644 device-software/meta-edison-middleware/recipes-devtools/iotkit-opkg/files/iotkit.conf create mode 100644 device-software/meta-edison-middleware/recipes-devtools/iotkit-opkg/iotkit-opkg_0.0.1.bb create mode 100644 device-software/meta-edison-middleware/recipes-devtools/mraa/mraa_0.5.2.bb create mode 100644 device-software/meta-edison-middleware/recipes-devtools/upm/upm_0.1.8.bb create mode 100644 device-software/meta-edison-middleware/recipes-devtools/xdk-daemon/xdk-daemon_0.0.27.bb create mode 100644 device-software/meta-edison/recipes-bsp/u-boot/files/edison.env create mode 100644 device-software/meta-edison/recipes-bsp/u-boot/files/fw_env.config create mode 100644 device-software/meta-edison/recipes-bsp/u-boot/files/target_env/blankcdc.env create mode 100644 device-software/meta-edison/recipes-bsp/u-boot/files/target_env/blankrndis.env create mode 100644 device-software/meta-edison/recipes-bsp/u-boot/files/target_env/defaultcdc.env create mode 100644 device-software/meta-edison/recipes-bsp/u-boot/files/target_env/defaultrndis.env create mode 100644 device-software/meta-edison/recipes-bsp/u-boot/files/target_env/ifwi.env create mode 100644 device-software/meta-edison/recipes-bsp/u-boot/files/target_env/prod.env create mode 100644 device-software/meta-edison/recipes-bsp/u-boot/files/upstream_to_edison.patch create mode 100644 device-software/meta-edison/recipes-bsp/u-boot/u-boot-fw-utils_2014.04.bb create mode 100644 device-software/meta-edison/recipes-bsp/u-boot/u-boot-internal.inc create mode 100644 device-software/meta-edison/recipes-bsp/u-boot/u-boot-osip.inc create mode 100644 device-software/meta-edison/recipes-bsp/u-boot/u-boot-target-env.inc create mode 100644 device-software/meta-edison/recipes-bsp/u-boot/u-boot-tools_2014.04.bb create mode 100644 device-software/meta-edison/recipes-bsp/u-boot/u-boot_2014.04.bb create mode 100644 device-software/utils/0001-busybox-handle-syslog-related-files-properly.patch create mode 100644 device-software/utils/0001-libarchive-avoid-dependency-on-e2fsprogs.patch create mode 100644 device-software/utils/0001-openssh-avoid-screen-sessions-being-killed-on-discon.patch create mode 100644 device-software/utils/Makefile.mk create mode 100755 device-software/utils/create-debian-image.sh create mode 100755 device-software/utils/handle_bash_func.patch create mode 100644 mw/oobe/package.json create mode 100644 mw/oobe/src/public/exit-upgrade.html create mode 100644 mw/oobe/src/public/upgrade.html diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..674b962 --- /dev/null +++ b/Makefile @@ -0,0 +1,182 @@ +# Top level makefile, repo tool should create a link on this file at the root +# of the build environement. + +.DEFAULT_GOAL := image + +# Parallelism is managed by bitbake for this project +.NOTPARALLEL: + +# In this makefile, all targets are phony because dependencies are managed at the bitbake level. +# We don't need to specify all targets here because no files named like them exist at the top level directory. +.PHONY : bbcache + +# Use a default build tag when none is set by the caller +NOWDATE := $(shell date +"%Y%m%d%H%M%S") +BUILD_TAG ?= custom_build_$(USER)@$(HOSTNAME)$(NOWDATE) +BB_DL_DIR ?= $(CURDIR)/bbcache/downloads +BB_SSTATE_DIR ?= $(CURDIR)/bbcache/sstate-cache + +############################################################################### +# Main targets +############################################################################### +setup: _setup-sdkhost_linux64 + +cleansstate: _check_setup_was_done + /bin/bash -c "source out/current/poky/oe-init-build-env $(CURDIR)/out/current/build ; $(CURDIR)/device-software/utils/invalidate_sstate.sh $(CURDIR)/out/current/build" + +devtools_package: _check_setup_was_done + /bin/bash -c "source out/current/poky/oe-init-build-env $(CURDIR)/out/current/build ; $(CURDIR)/device-software/utils/create_devtools_package.sh $(CURDIR)/out/current/build" + +sdk: _check_setup_was_done + /bin/bash -c "source out/current/poky/oe-init-build-env $(CURDIR)/out/current/build ; bitbake edison-image -c populate_sdk" + +src-package: pub + ./device-software/utils/create_src_package.sh + mv edison-src.tgz $(CURDIR)/pub/edison-src-$(BUILD_TAG).tgz + +clean: + rm -rf out + +u-boot linux-externalsrc edison-image meta-toolchain bootimg: _check_setup_was_done + /bin/bash -c "source out/current/poky/oe-init-build-env $(CURDIR)/out/current/build ; bitbake -c cleansstate $@ ; bitbake $@" + ./device-software/utils/flash/postBuild.sh $(CURDIR)/out/current/build + +bootloader: u-boot + +image: edison-image + +kernel: linux-externalsrc bootimg + +toolchain: meta-toolchain + +flash: _check_postbuild_was_done + ./out/current/build/toFlash/flashall.sh + +flash-kernel: _check_postbuild_was_done + dd if=./out/current/build/toFlash/edison-image-edison.hddimg | ssh root@192.168.2.15 "dd of=/dev/disk/by-partlabel/boot bs=1M" + ssh root@192.168.2.15 "/sbin/reboot -f" + +flash-bootloader: _check_postbuild_was_done + dfu-util -d 8087:0a99 --alt u-boot0 -D ./out/current/build/toFlash/u-boot-edison.bin -R + +cscope: + find linux-kernel/ u-boot -regex '.*\.\(c\|cpp\|h\)$\' > cscope.files + cscope -R -b -k + +list: + @sh -c "$(MAKE) -p _no_targets | awk -F':' '/^[a-zA-Z0-9][^\$$#\/\\t=]*:([^=]|$$)/ {split(\$$1,A,/ /);for(i in A)print A[i]}' | sort" + +debian_image: + $(MAKE) setup SETUP_ARGS="$(SETUP_ARGS) --deb_packages" + $(MAKE) image + @echo '*******************************' + @echo '*******************************' + @echo 'Now run the following command to create the debian rootfs:' + @echo 'sudo $(CURDIR)/device-software/utils/create-debian-image.sh --build_dir=$(CURDIR)/out/current/build' + @echo 'and run a regular make flash' + @echo '*******************************' + +help: + @echo 'Main targets:' + @echo ' help - show this help' + @echo ' clean - remove the out and pub directory' + @echo ' setup - prepare the build env for later build operations' + @echo ' cleansstate - clean the sstate for some recipes to work-around some bitbake limitations' + @echo ' image - build the flashable edison image, results are in out/current/build/toFlash' + @echo ' flash - flash the current build image' + @echo ' sdk - build the SDK for the current build' + @echo ' toolchain - build the cross compilation toolchain for the current build' + @echo ' src-package - create the external source package' + @echo ' devtools_package - build some extra dev tools packages, results are in out/current/build/devtools_packages/' + @echo + @echo 'Continuous Integration targets:' + @sh -c "$(MAKE) -p _no_targets | awk -F':' '/^ci_[a-zA-Z0-9][^\$$#\/\\t=]*:([^=]|$$)/ {split(\$$1,A,/ /);for(i in A)print \" \"A[i]}' | sort" + @echo + @echo 'Environment variables:' + @echo ' BUILD_TAG - set the build name used for e.g. artifact file naming' + @echo ' BB_DL_DIR - defines the directory (absolute path) where bitbake places downloaded files (defaults to bbcache/downloads)' + @echo ' BB_SSTATE_DIR - defines the directory (absolute path) where bitbake places shared-state files (defaults to bbcache/sstate-cache)' + @echo ' SETUP_ARGS - control advanced behaviour of the setup script (run ./device-software/setup.sh --help for more details)' + +############################################################################### +# Private targets +############################################################################### + +_no_targets: + +_check_setup_was_done: + @if [ ! -f $(CURDIR)/out/current/build/conf/local.conf ]; then echo Please run \"make setup\" first ; exit 1 ; fi + +_check_postbuild_was_done: + @if [ ! -f $(CURDIR)/out/current/build/toFlash/flashall.sh ]; then echo Please run \"make image/bootloader/kernel\" first ; exit 1 ; fi + +_setup-sdkhost_%: pub bbcache + @echo Setup buildenv for SDK host $* + @mkdir -p out/$* + ./device-software/setup.sh $(SETUP_ARGS) --dl_dir=$(BB_DL_DIR) --sstate_dir=$(BB_SSTATE_DIR) --build_dir=$(CURDIR)/out/$* --build_name=$(BUILD_TAG) --sdk_host=$* + @rm -f out/current + @ln -s $(CURDIR)/out/$* $(CURDIR)/out/current + @if [ $* = macosx ]; then /bin/bash -c "source out/current/poky/oe-init-build-env $(CURDIR)/out/current/build ; bitbake odcctools2-crosssdk -c cleansstate" ; echo "Please make sure that OSX-sdk.zip is available in your bitbake download directory" ; fi + +pub: + @mkdir -p $@ + +bbcache: + @mkdir -p bbcache + @mkdir -p $(BB_DL_DIR) + @mkdir -p $(BB_SSTATE_DIR) + +_image_archive: + cd $(CURDIR)/out/current/build/toFlash ; zip -r $(CURDIR)/pub/edison-image-$(BUILD_TAG).zip `ls` + cd $(CURDIR)/out/current/build/symbols ; zip -r $(CURDIR)/pub/symbols-$(BUILD_TAG).zip `ls` + +_devtools_package_archive: + cd $(CURDIR)/out/current/build/devtools_packages ; zip -r $(CURDIR)/pub/edison-devtools-packages-$(BUILD_TAG).zip `ls` + +_sdk_archive_%: + cd $(CURDIR)/out/$*/build/tmp/deploy/sdk ; zip -r $(CURDIR)/pub/edison-sdk-$*-$(BUILD_TAG).zip `ls *-edison-image-*` + +_toolchain_archive_%: + cd $(CURDIR)/out/$*/build/tmp/deploy/sdk ; zip -r $(CURDIR)/pub/edison-meta-toolchain-$*-$(BUILD_TAG).zip `ls *-meta-toolchain-*` + + +############################################################################### +# Continuous Integration targets: one per checkbox available in jenkins +# Each target places the the end-user artifact in the pub/ directory +############################################################################### + +ci_image: setup cleansstate devtools_package _devtools_package_archive image _image_archive + +_ci_sdk_%: + $(MAKE) _setup-sdkhost_$* cleansstate sdk _sdk_archive_$* + +_ci_toolchain_%: + $(MAKE) _setup-sdkhost_$* cleansstate toolchain _toolchain_archive_$* + +ci_sdk_win32: _ci_sdk_win32 +ci_sdk_win64: _ci_sdk_win64 +ci_sdk_linux32: _ci_sdk_linux32 +ci_sdk_linux64: _ci_sdk_linux64 +ci_sdk_macosx: _ci_sdk_macosx +ci_toolchain_win32: _ci_toolchain_win32 +ci_toolchain_win64: _ci_toolchain_win64 +ci_toolchain_linux32: _ci_toolchain_linux32 +ci_toolchain_linux64: _ci_toolchain_linux64 +ci_toolchain_macosx: _ci_toolchain_macosx + +ci_image-from-src-package-and-GPL-LGPL-sources_archive: setup src-package + cp $(CURDIR)/pub/edison-src-$(BUILD_TAG).tgz $(CURDIR)/out/current + cd $(CURDIR)/out/current ; tar -xvf edison-src-$(BUILD_TAG).tgz + cd $(CURDIR)/out/current/edison-src ; /bin/bash -c "SETUP_ARGS=\"$(SETUP_ARGS) --create_src_archive\" make setup cleansstate image _image_archive" + cd $(CURDIR)/out/current/edison-src/out/current/build/toFlash ; zip -r $(CURDIR)/pub/edison-image-from-src-package-$(BUILD_TAG).zip `ls` + cd $(CURDIR)/out/current/edison-src/out/current/build/tmp/deploy/sources ; zip -r $(CURDIR)/pub/edison-GPL_LGPL-sources-$(BUILD_TAG).zip `ls` + +ci_full: + $(MAKE) ci_image BUILD_TAG=$(BUILD_TAG) + $(MAKE) ci_image-from-src-package-and-GPL-LGPL-sources_archive BUILD_TAG=$(BUILD_TAG) + $(MAKE) ci_sdk_win32 ci_toolchain_win32 BUILD_TAG=$(BUILD_TAG) + $(MAKE) ci_sdk_win64 ci_toolchain_win64 BUILD_TAG=$(BUILD_TAG) + $(MAKE) ci_sdk_linux32 ci_toolchain_linux32 BUILD_TAG=$(BUILD_TAG) + $(MAKE) ci_sdk_linux64 ci_toolchain_linux64 BUILD_TAG=$(BUILD_TAG) + $(MAKE) ci_sdk_macosx ci_toolchain_macosx BUILD_TAG=$(BUILD_TAG) + diff --git a/arduino/clloader/clloader.c b/arduino/clloader/clloader.c index f2a6952..73a0296 100644 --- a/arduino/clloader/clloader.c +++ b/arduino/clloader/clloader.c @@ -2729,12 +2729,15 @@ int clantonLoaderFSM(int argc, char * argv[]) fprintf(stderr, "host: %s\n", rbuf_from_host); errors = 0; /* TODO: bring out state change of BAUD/LINE and replace magic string */ - if (strncmp(CL_LOADER_CMD_HOST_START_DOWNLOAD_CMD, - rbuf_from_host, - (size_t)strlen(CL_LOADER_CMD_HOST_START_DOWNLOAD_CMD)) == 0) { + if (strncmp(downloadCMD, rbuf_from_host, + ((size_t)strlen(rbuf_from_host)-1)) == 0) { // We got a special command to start download clantonLeavePassThroughState = TRUE; - + } else if (strncmp(CL_LOADER_CMD_HOST_START_DOWNLOAD_CMD, + rbuf_from_host, + ((size_t)strlen(rbuf_from_host)-1)) == 0) { + // Old command string. Allow to download anyway. + clantonLeavePassThroughState = TRUE; } else { write(mystate.tty_slave,rbuf_from_host,ret); //todo check write status } diff --git a/arduino/clloader/clloader.h b/arduino/clloader/clloader.h index c9dd595..749672f 100644 --- a/arduino/clloader/clloader.h +++ b/arduino/clloader/clloader.h @@ -22,6 +22,7 @@ #define MAX_ARGS 0x20 #define CL_LOADER_CMD_HOST_START_DOWNLOAD_CMD "~sketch download" +#define downloadCMD "~sketch downloadEdison" #define CL_LOADER_CMD_STARTCHAR '#' #define CL_LOADER_CMD_START_SKETCH_APP "#Start Sketch:" /* filename args and exec i/o wrapper */ diff --git a/broadcom_cws/bluetooth/firmware/BCM43341B0_002.001.014.0123.0168.hcd b/broadcom_cws/bluetooth/firmware/BCM43341B0_002.001.014.0123.0168.hcd new file mode 100644 index 0000000000000000000000000000000000000000..d20e2e2c825b75afc4764ceab6240c757a1ea7ea GIT binary patch literal 41773 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 i++) { c = dtoh32(list->element[i]); + c = wl_chspec_driver_to_host(c); if (channel <= CH_MAX_2G_CHANNEL) { if (!CHSPEC_IS20(c)) continue; @@ -2657,6 +2658,8 @@ channel_to_chanspec(struct wiphy *wiphy, struct net_device *dev, u32 channel, u3 if (buf) kfree(buf); #undef LOCAL_BUF_SIZE + if (ret_c) + ret_c = wl_chspec_host_to_driver(ret_c); WL_INFO(("return chanspec %x %d\n", ret_c, bw)); return ret_c; } @@ -2738,7 +2741,10 @@ wl_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, s32 err = 0; size_t join_params_size; chanspec_t chanspec = 0; - u32 param[2] = {0, 0}; + struct { + u32 band; + u32 bw_cap; + } param = {0, 0}; u32 bw_cap = 0; WL_TRACE(("In\n")); @@ -2808,16 +2814,35 @@ wl_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, cfg->ibss_starter = true; } if (chan) { - if (chan->band == IEEE80211_BAND_5GHZ) - param[0] = WLC_BAND_5G; - else if (chan->band == IEEE80211_BAND_2GHZ) - param[0] = WLC_BAND_2G; - err = wldev_iovar_getint(dev, "bw_cap", param); - if (unlikely(err)) { - WL_ERR(("Get bw_cap Failed (%d)\n", err)); - return err; + if (chan->band == IEEE80211_BAND_5GHZ) { + param.band = WLC_BAND_5G; + err = wldev_iovar_getbuf(dev, "bw_cap", ¶m, sizeof(param), + cfg->ioctl_buf, WLC_IOCTL_SMLEN, &cfg->ioctl_buf_sync); + if (err) { + if (err != BCME_UNSUPPORTED) { + WL_ERR(("bw_cap failed, %d\n", err)); + return err; + } else { + err = wldev_iovar_getint(dev, "mimo_bw_cap", &bw_cap); + if (err) { + WL_ERR(("error get mimo_bw_cap (%d)\n", err)); + } + if (bw_cap != WLC_N_BW_20ALL) + bw_cap = WL_CHANSPEC_BW_40; + } + } + else { + if (WL_BW_CAP_80MHZ(cfg->ioctl_buf[0])) + bw_cap = WL_CHANSPEC_BW_80; + else if (WL_BW_CAP_40MHZ(cfg->ioctl_buf[0])) + bw_cap = WL_CHANSPEC_BW_40; + else + bw_cap = WL_CHANSPEC_BW_20; + } } - bw_cap = param[0]; + else if (chan->band == IEEE80211_BAND_2GHZ) + bw_cap = WL_CHANSPEC_BW_20; + chanspec = channel_to_chanspec(wiphy, dev, cfg->channel, bw_cap); } /* @@ -2851,9 +2876,11 @@ wl_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, } } - join_params.params.chanspec_list[0] = chanspec; - join_params.params.chanspec_num = 1; - wldev_iovar_setint(dev, "chanspec", chanspec); + if (chan) { + join_params.params.chanspec_list[0] = chanspec; + join_params.params.chanspec_num = 1; + wldev_iovar_setint(dev, "chanspec", chanspec); + } join_params_size = sizeof(join_params); /* Disable Authentication, IBSS will add key if it required */ diff --git a/device-software/meta-edison-devtools/recipes-connectivity/bcm43340-tools/dhdutil.bb b/device-software/meta-edison-devtools/recipes-connectivity/bcm43340-tools/dhdutil.bb new file mode 100644 index 0000000..3f08096 --- /dev/null +++ b/device-software/meta-edison-devtools/recipes-connectivity/bcm43340-tools/dhdutil.bb @@ -0,0 +1,15 @@ +SUMMARY = "dhdutil utility" +DESCRIPTION = "dhdutil utility for BCM chipset" +SECTION = "test-tools" +LICENSE = "CLOSED" + +PV = "r1.141" +PR = "r47" + +S = "${EDISONREPO_TOP_DIR}/broadcom_tools/wlan/dhdutil" + +do_install() { + install -v -d ${D}/usr/sbin/ + install -m 0755 ${B}/dhdutil ${D}/usr/sbin +} + diff --git a/device-software/meta-edison-devtools/recipes-connectivity/bcm43340-tools/wlx.bb b/device-software/meta-edison-devtools/recipes-connectivity/bcm43340-tools/wlx.bb new file mode 100644 index 0000000..3d88e6e --- /dev/null +++ b/device-software/meta-edison-devtools/recipes-connectivity/bcm43340-tools/wlx.bb @@ -0,0 +1,19 @@ +SUMMARY = "wlx utility" +DESCRIPTION = "wlx utility for BCM chipset" +SECTION = "test-tools" +LICENSE = "CLOSED" + +PV = "r6.10.190" +PR = "r40" + +S = "${EDISONREPO_TOP_DIR}/broadcom_tools/wlan/wlx" + +do_compile () { + oe_runmake -C wl/exe +} + +do_install() { + install -v -d ${D}/usr/sbin/ + install -m 0755 ${B}/wl/exe/wlx ${D}/usr/sbin +} + diff --git a/device-software/meta-edison-devtools/recipes-connectivity/wfa-tool/wfa-tool.bb b/device-software/meta-edison-devtools/recipes-connectivity/wfa-tool/wfa-tool.bb index 24a78e1..1d21cfe 100644 --- a/device-software/meta-edison-devtools/recipes-connectivity/wfa-tool/wfa-tool.bb +++ b/device-software/meta-edison-devtools/recipes-connectivity/wfa-tool/wfa-tool.bb @@ -20,8 +20,11 @@ do_install() { install -m 0755 ${S}/scripts/wfa_stop_ping ${D}/usr/sbin install -m 0755 ${S}/scripts/wfa_start_dhcp_client ${D}/usr/sbin install -m 0755 ${S}/scripts/wfa_stop_dhcp_client ${D}/usr/sbin + install -m 0755 ${S}/scripts/wfa_start_dhcp_server ${D}/usr/sbin + install -m 0755 ${S}/scripts/wfa_stop_dhcp_server ${D}/usr/sbin install -v -d ${D}${sysconfdir}/sigma + install -m 644 ${S}/scripts/udhcpd.conf ${D}${sysconfdir}/sigma install -m 644 ${S}/certificates/cas.pem ${D}${sysconfdir}/sigma install -m 644 ${S}/certificates/root.pem ${D}${sysconfdir}/sigma install -m 644 ${S}/certificates/wifiuser.pem ${D}${sysconfdir}/sigma diff --git a/device-software/meta-edison-distro/conf/distro/poky-edison.conf b/device-software/meta-edison-distro/conf/distro/poky-edison.conf index 3a9c247..a0e7466 100644 --- a/device-software/meta-edison-distro/conf/distro/poky-edison.conf +++ b/device-software/meta-edison-distro/conf/distro/poky-edison.conf @@ -6,8 +6,8 @@ PREFERRED_VERSION_linux-yocto = "3.10%" PREFERRED_PROVIDER_virtual/bootloader ?= "u-boot" PREFERRED_VERSION_u-boot ?= "2014.04-1" PREFERRED_VERSION_u-boot-fw-utils ?= "2014.04-1" -PREFERRED_VERSION_connman ?= "1.24" -PREFERRED_VERSION_openssl ?= "1.0.1g" +PREFERRED_VERSION_connman ?= "1.27" +PREFERRED_VERSION_openssl ?= "1.0.1j" PREFERRED_VERSION_systemd ?= "213+gitAUTOINC+c9679c652b" DISTRO_FEATURES = "systemd alsa argp bluetooth ext2 largefile usbgadget usbhost wifi xattr nfs zeroconf pci ${DISTRO_FEATURES_LIBC}" diff --git a/device-software/meta-edison-distro/recipes-connectivity/bluetooth-rfkill-event/files/bcm43341.conf b/device-software/meta-edison-distro/recipes-connectivity/bluetooth-rfkill-event/files/bcm43341.conf index dc4ea4f..d43b051 100644 --- a/device-software/meta-edison-distro/recipes-connectivity/bluetooth-rfkill-event/files/bcm43341.conf +++ b/device-software/meta-edison-distro/recipes-connectivity/bluetooth-rfkill-event/files/bcm43341.conf @@ -28,5 +28,5 @@ fw_patch = /etc/firmware/bcm43341.hcd uart_dev = /dev/ttyMFD0 # SCO settings -# N/A now -# scopcm = +# configure sco routing to Transport (HCI); +scopcm = 1,0,0,0,0,0,0,0,0,0 diff --git a/device-software/meta-edison-distro/recipes-connectivity/bluetooth-rfkill-event/files/bluetooth_rfkill_event.c b/device-software/meta-edison-distro/recipes-connectivity/bluetooth-rfkill-event/files/bluetooth_rfkill_event.c index 368328c..9d69805 100644 --- a/device-software/meta-edison-distro/recipes-connectivity/bluetooth-rfkill-event/files/bluetooth_rfkill_event.c +++ b/device-software/meta-edison-distro/recipes-connectivity/bluetooth-rfkill-event/files/bluetooth_rfkill_event.c @@ -298,24 +298,25 @@ void load_bd_add(void) fp = fopen(BD_ADD_FACTORY_FILE, "r"); - /* if BD add file has not been provisioned use default one */ + /* if BD add file has not been provisioned do not send VSC to set BD address: the one configured in OTP will be used or default FW one */ if (fp == NULL) { - memcpy(factory_bd_add, default_bd_addr, sizeof(factory_bd_add)); - main_opts.bd_add = factory_bd_add; - main_opts.set_bd = TRUE; + main_opts.set_bd = FALSE; return; } ret = fscanf(fp, "%17c", factory_bd_add); - /* if factory BD address is not well formatted or not present use default one*/ + /* if factory BD address is not well formatted or not present do not send VSC to set BD address: the one configured in OTP will be used or default FW one */ if (!(ret == 1 && check_bd_format(factory_bd_add))) { - memcpy(factory_bd_add, default_bd_addr, sizeof(factory_bd_add)); + main_opts.set_bd = FALSE; + } + else + { + main_opts.bd_add = factory_bd_add; + main_opts.set_bd = TRUE; } - main_opts.bd_add = factory_bd_add; - main_opts.set_bd = TRUE; fclose(fp); diff --git a/device-software/meta-edison-distro/recipes-connectivity/bluez5/bluez5_5.15.bbappend b/device-software/meta-edison-distro/recipes-connectivity/bluez5/bluez5_5.15.bbappend index a4672a5..3aad093 100644 --- a/device-software/meta-edison-distro/recipes-connectivity/bluez5/bluez5_5.15.bbappend +++ b/device-software/meta-edison-distro/recipes-connectivity/bluez5/bluez5_5.15.bbappend @@ -1,19 +1,22 @@ -# overwrite to 5.18 version and its checksum +# overwrite to 5.24 version and its checksum -PV = "5.18" +PV = "5.24" -SRC_URI[md5sum] = "9c71404f86287cd9441e86783be3aae1" -SRC_URI[sha256sum] = "8c8bb13db83d0de6c85def2da8dfa0a758aff595405fb57a00719ed94d558340" +SRC_URI[md5sum] = "37b785185fb98269b45e51b254bd8d3d" +SRC_URI[sha256sum] = "e870c5fba0bf3496856fc720e2d217856fcf40b59829f8cc0c05902ebb9fb837" # to get bluetooth.conf FILESEXTRAPATHS_prepend := "${THISDIR}/files/" -# few overwrite for 5.18 version +# few overwrite for 5.24 version SRC_URI = "\ ${KERNELORG_MIRROR}/linux/bluetooth/bluez-${PV}.tar.xz \ file://bluetooth.conf \ + file://obex_set_dbus_session_service.patch \ " +RDEPENDS_${PN} += "eglibc-gconv-utf-16" + PACKAGECONFIG[alsa] = "" EXTRA_OECONF = "\ @@ -24,6 +27,7 @@ EXTRA_OECONF = "\ --enable-datafiles \ ${@base_contains('DISTRO_FEATURES', 'systemd', '--with-systemdsystemunitdir=${systemd_unitdir}/system/', '--disable-systemd', d)} \ --enable-library \ + --enable-experimental \ " do_install_append() { @@ -43,6 +47,16 @@ do_install_append() { if [ -f ${S}/src/main.conf ]; then install -m 0644 ${S}/src/main.conf ${D}/${sysconfdir}/bluetooth/ fi + if [ -f ${S}/tools/obexctl ]; then + install -m 0755 ${S}/tools/obexctl ${D}${bindir} + fi + + if ${@base_contains('DISTRO_FEATURES','systemd','true','false',d)}; then + # Copy file service + install -d ${D}/${systemd_unitdir}/system + install -m 644 ${S}/obexd/src/obex.service ${D}/${systemd_unitdir}/system/ + fi + # at_console doesn't really work with the current state of OE, so punch some more holes so people can actually use BT install -m 0644 ${WORKDIR}/bluetooth.conf ${D}/${sysconfdir}/dbus-1/system.d/ } diff --git a/device-software/meta-edison-distro/recipes-connectivity/bluez5/files/bluetooth.conf b/device-software/meta-edison-distro/recipes-connectivity/bluez5/files/bluetooth.conf index e21e72e..162c803 100644 --- a/device-software/meta-edison-distro/recipes-connectivity/bluez5/files/bluetooth.conf +++ b/device-software/meta-edison-distro/recipes-connectivity/bluez5/files/bluetooth.conf @@ -9,8 +9,16 @@ + + + + + + + + diff --git a/device-software/meta-edison-distro/recipes-connectivity/bluez5/files/obex_set_dbus_session_service.patch b/device-software/meta-edison-distro/recipes-connectivity/bluez5/files/obex_set_dbus_session_service.patch new file mode 100644 index 0000000..4000aaf --- /dev/null +++ b/device-software/meta-edison-distro/recipes-connectivity/bluez5/files/obex_set_dbus_session_service.patch @@ -0,0 +1,10 @@ +--- a/obexd/src/obex.service.in 2014-10-30 14:40:00.832479049 +0100 ++++ b/obexd/src/obex.service.in 2014-10-30 14:40:41.032477622 +0100 +@@ -4,6 +4,7 @@ + [Service] + Type=dbus + BusName=org.bluez.obex ++Environment="DBUS_SESSION_BUS_ADDRESS=unix:path=/var/run/dbus/system_bus_socket" + ExecStart=@libexecdir@/obexd + + [Install] diff --git a/device-software/meta-edison-distro/recipes-connectivity/connman/connman/disable_p2p.patch b/device-software/meta-edison-distro/recipes-connectivity/connman/connman/disable_p2p.patch new file mode 100644 index 0000000..6cce2e3 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-connectivity/connman/connman/disable_p2p.patch @@ -0,0 +1,15 @@ +--- a/gsupplicant/supplicant.c ++++ b/gsupplicant/supplicant.c +@@ -1963,8 +1963,11 @@ + if (g_strcmp0(key, "Capabilities") == 0) { + supplicant_dbus_property_foreach(iter, interface_capability, + interface); ++#if 0 ++ /* Disable temporarely p2p */ + if (interface->mode_capa & G_SUPPLICANT_CAPABILITY_MODE_P2P) + interface->p2p_support = true; ++#endif + } else if (g_strcmp0(key, "State") == 0) { + const char *str = NULL; + + diff --git a/device-software/meta-edison-distro/recipes-connectivity/connman/connman_1.27.bb b/device-software/meta-edison-distro/recipes-connectivity/connman/connman_1.27.bb new file mode 100644 index 0000000..70993ac --- /dev/null +++ b/device-software/meta-edison-distro/recipes-connectivity/connman/connman_1.27.bb @@ -0,0 +1,11 @@ +require connman.inc + +SRC_URI = "${KERNELORG_MIRROR}/linux/network/${BPN}/${BP}.tar.xz \ + file://0001-plugin.h-Change-visibility-to-default-for-debug-symb.patch \ + file://add_xuser_dbus_permission.patch \ + file://disable_p2p.patch \ + file://connman \ + " +SRC_URI[md5sum] = "4f4b3be54da000c65b153c1b9afcadf2" +SRC_URI[sha256sum] = "13997824c076af150c68d6d79e48277216e8192278a5c6615cfd4905d65600f5" +RRECOMMENDS_${PN} = "connman-conf" diff --git a/device-software/meta-edison-distro/recipes-connectivity/connman/connman_1.27.bbappend b/device-software/meta-edison-distro/recipes-connectivity/connman/connman_1.27.bbappend new file mode 100644 index 0000000..80e7b38 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-connectivity/connman/connman_1.27.bbappend @@ -0,0 +1,35 @@ + + +LIC_FILES_CHKSUM = "file://COPYING;md5=12f884d2ae1ff87c09e5b7ccc2c4ca7e \ + file://src/main.c;beginline=1;endline=20;md5=486a279a6ab0c8d152bcda3a5b5edc36" + +PACKAGECONFIG ??= "wispr \ + ${@base_contains('DISTRO_FEATURES', 'wifi','wifi', '', d)} \ + ${@base_contains('DISTRO_FEATURES', 'bluetooth','bluetooth', '', d)} \ +" + +PACKAGECONFIG[bluetooth] = "--enable-bluetooth, --disable-bluetooth, bluez5" +PACKAGECONFIG[3g] = "" + +SYSTEMD_AUTO_ENABLE = "disable" + +do_configure_append () { + # Do not usb0 as it is used for SSH connection + sed -i "s/ExecStart=.*/& --nodevice=usb0/" ${S}/src/connman.service +} + + +# These used to be plugins, but now they are core +RPROVIDES_${PN} = "\ + connman-plugin-loopback \ + connman-plugin-ethernet \ + ${@base_contains('PACKAGECONFIG', 'bluetooth','connman-plugin-bluetooth', '', d)} \ + ${@base_contains('PACKAGECONFIG', 'wifi','connman-plugin-wifi', '', d)} \ + " + +RDEPENDS_${PN} = "\ + dbus \ + ${@base_contains('PACKAGECONFIG', 'bluetooth', 'bluez5', '', d)} \ + ${@base_contains('PACKAGECONFIG', 'wifi','wpa-supplicant', '', d)} \ + " + diff --git a/device-software/meta-edison-distro/recipes-connectivity/hostapd/files/hostapd.conf-sane b/device-software/meta-edison-distro/recipes-connectivity/hostapd/files/hostapd.conf-sane index da81096..3e7d794 100644 --- a/device-software/meta-edison-distro/recipes-connectivity/hostapd/files/hostapd.conf-sane +++ b/device-software/meta-edison-distro/recipes-connectivity/hostapd/files/hostapd.conf-sane @@ -424,7 +424,7 @@ wmm_ac_vo_acm=0 # 0 = disabled (default) # 1 = enabled # Note: You will also need to enable WMM for full HT functionality. -#ieee80211n=1 +ieee80211n=1 # ht_capab: HT capabilities (list of flags) # LDPC coding capability: [LDPC] = supported diff --git a/device-software/meta-edison-distro/recipes-connectivity/hostapd/files/udhcpd-for-hostapd.conf b/device-software/meta-edison-distro/recipes-connectivity/hostapd/files/udhcpd-for-hostapd.conf index e25d704..1015e39 100644 --- a/device-software/meta-edison-distro/recipes-connectivity/hostapd/files/udhcpd-for-hostapd.conf +++ b/device-software/meta-edison-distro/recipes-connectivity/hostapd/files/udhcpd-for-hostapd.conf @@ -83,7 +83,7 @@ pidfile /var/run/udhcpd-wlan0.pid #default: /var/run/udhcpd.pid # lines. The only option with a default is 'lease'. # Currently supported options, for more info, see options.c -#opt subnet +opt subnet 255.255.255.0 #opt timezone #opt router #opt timesvr diff --git a/device-software/meta-edison-distro/recipes-connectivity/openssh/openssh/sshdgenkeys.service b/device-software/meta-edison-distro/recipes-connectivity/openssh/openssh/sshdgenkeys.service index 25d27ae..e8133e4 100644 --- a/device-software/meta-edison-distro/recipes-connectivity/openssh/openssh/sshdgenkeys.service +++ b/device-software/meta-edison-distro/recipes-connectivity/openssh/openssh/sshdgenkeys.service @@ -2,6 +2,6 @@ Description=OpenSSH Key Generation [Service] -ExecStart=@BASE_BINDIR@/sh -c "if ! sshd -t > /dev/null ; then rm /etc/ssh/*_key* ; ssh-keygen -A ; sync ; fi" +ExecStart=@BASE_BINDIR@/sh -c "if ! sshd -t &> /dev/null ; then rm /etc/ssh/*_key* ; ssh-keygen -A ; sync ; fi" Type=oneshot RemainAfterExit=yes diff --git a/device-software/meta-edison-distro/recipes-connectivity/openssl/openssl/openssl-1.0.1e-cve-2014-0195.patch b/device-software/meta-edison-distro/recipes-connectivity/openssl/openssl/openssl-1.0.1e-cve-2014-0195.patch new file mode 100644 index 0000000..0c43919 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-connectivity/openssl/openssl/openssl-1.0.1e-cve-2014-0195.patch @@ -0,0 +1,40 @@ +commit 208d54db20d58c9a5e45e856a0650caadd7d9612 +Author: Dr. Stephen Henson +Date: Tue May 13 18:48:31 2014 +0100 + + Fix for CVE-2014-0195 + + A buffer overrun attack can be triggered by sending invalid DTLS fragments + to an OpenSSL DTLS client or server. This is potentially exploitable to + run arbitrary code on a vulnerable client or server. + + Fixed by adding consistency check for DTLS fragments. + + Thanks to Jüri Aedla for reporting this issue. + +Patch borrowed from Fedora +Upstream-Status: Backport +Signed-off-by: Paul Eggleton + +diff --git a/ssl/d1_both.c b/ssl/d1_both.c +index 2e8cf68..07f67f8 100644 +--- a/ssl/d1_both.c ++++ b/ssl/d1_both.c +@@ -627,7 +627,16 @@ dtls1_reassemble_fragment(SSL *s, struct hm_header_st* msg_hdr, int *ok) + frag->msg_header.frag_off = 0; + } + else ++ { + frag = (hm_fragment*) item->data; ++ if (frag->msg_header.msg_len != msg_hdr->msg_len) ++ { ++ item = NULL; ++ frag = NULL; ++ goto err; ++ } ++ } ++ + + /* If message is already reassembled, this must be a + * retransmit and can be dropped. + diff --git a/device-software/meta-edison-distro/recipes-connectivity/openssl/openssl/openssl-1.0.1e-cve-2014-0198.patch b/device-software/meta-edison-distro/recipes-connectivity/openssl/openssl/openssl-1.0.1e-cve-2014-0198.patch new file mode 100644 index 0000000..12dcfb7 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-connectivity/openssl/openssl/openssl-1.0.1e-cve-2014-0198.patch @@ -0,0 +1,38 @@ +From: Matt Caswell +Date: Sun, 11 May 2014 23:38:37 +0000 (+0100) +Subject: Fixed NULL pointer dereference. See PR#3321 +X-Git-Url: https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff_plain;h=b107586 + +Fixed NULL pointer dereference. See PR#3321 + +Patch borrowed from Fedora +Upstream-Status: Backport +Signed-off-by: Paul Eggleton + +--- + +diff --git a/ssl/s3_pkt.c b/ssl/s3_pkt.c +index 40eb0dd..d961d12 100644 +--- a/ssl/s3_pkt.c ++++ b/ssl/s3_pkt.c +@@ -657,9 +657,6 @@ static int do_ssl3_write(SSL *s, int type, const unsigned char *buf, + SSL3_BUFFER *wb=&(s->s3->wbuf); + SSL_SESSION *sess; + +- if (wb->buf == NULL) +- if (!ssl3_setup_write_buffer(s)) +- return -1; + + /* first check if there is a SSL3_BUFFER still being written + * out. This will happen with non blocking IO */ +@@ -675,6 +672,10 @@ static int do_ssl3_write(SSL *s, int type, const unsigned char *buf, + /* if it went, fall through and send more stuff */ + } + ++ if (wb->buf == NULL) ++ if (!ssl3_setup_write_buffer(s)) ++ return -1; ++ + if (len == 0 && !create_empty_fragment) + return 0; + diff --git a/device-software/meta-edison-distro/recipes-connectivity/openssl/openssl/openssl-1.0.1e-cve-2014-0221.patch b/device-software/meta-edison-distro/recipes-connectivity/openssl/openssl/openssl-1.0.1e-cve-2014-0221.patch new file mode 100644 index 0000000..bf730a8 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-connectivity/openssl/openssl/openssl-1.0.1e-cve-2014-0221.patch @@ -0,0 +1,38 @@ +commit d30e582446b027868cdabd0994681643682045a4 +Author: Dr. Stephen Henson +Date: Fri May 16 13:00:45 2014 +0100 + + Fix CVE-2014-0221 + + Unnecessary recursion when receiving a DTLS hello request can be used to + crash a DTLS client. Fixed by handling DTLS hello request without recursion. + + Thanks to Imre Rad (Search-Lab Ltd.) for discovering this issue. + +Patch borrowed from Fedora +Upstream-Status: Backport +Signed-off-by: Paul Eggleton + +diff --git a/ssl/d1_both.c b/ssl/d1_both.c +index 07f67f8..4c2fd03 100644 +--- a/ssl/d1_both.c ++++ b/ssl/d1_both.c +@@ -793,6 +793,7 @@ dtls1_get_message_fragment(SSL *s, int st1, int stn, long max, int *ok) + int i,al; + struct hm_header_st msg_hdr; + ++ redo: + /* see if we have the required fragment already */ + if ((frag_len = dtls1_retrieve_buffered_fragment(s,max,ok)) || *ok) + { +@@ -851,8 +852,7 @@ dtls1_get_message_fragment(SSL *s, int st1, int stn, long max, int *ok) + s->msg_callback_arg); + + s->init_num = 0; +- return dtls1_get_message_fragment(s, st1, stn, +- max, ok); ++ goto redo; + } + else /* Incorrectly formated Hello request */ + { + diff --git a/device-software/meta-edison-distro/recipes-connectivity/openssl/openssl/openssl-1.0.1e-cve-2014-0224.patch b/device-software/meta-edison-distro/recipes-connectivity/openssl/openssl/openssl-1.0.1e-cve-2014-0224.patch new file mode 100644 index 0000000..0ed1d12 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-connectivity/openssl/openssl/openssl-1.0.1e-cve-2014-0224.patch @@ -0,0 +1,103 @@ +Fix for CVE-2014-0224 + +Only accept change cipher spec when it is expected instead of at any +time. This prevents premature setting of session keys before the master +secret is determined which an attacker could use as a MITM attack. + +Thanks to KIKUCHI Masashi (Lepidum Co. Ltd.) for reporting this issue +and providing the initial fix this patch is based on. + + +Patch borrowed from Fedora +Upstream-Status: Backport +Signed-off-by: Paul Eggleton + + +diff -up openssl-1.0.1e/ssl/ssl3.h.keying-mitm openssl-1.0.1e/ssl/ssl3.h +--- openssl-1.0.1e/ssl/ssl3.h.keying-mitm 2014-06-02 19:48:04.518100562 +0200 ++++ openssl-1.0.1e/ssl/ssl3.h 2014-06-02 19:48:04.642103429 +0200 +@@ -388,6 +388,7 @@ typedef struct ssl3_buffer_st + #define TLS1_FLAGS_TLS_PADDING_BUG 0x0008 + #define TLS1_FLAGS_SKIP_CERT_VERIFY 0x0010 + #define TLS1_FLAGS_KEEP_HANDSHAKE 0x0020 ++#define SSL3_FLAGS_CCS_OK 0x0080 + + /* SSL3_FLAGS_SGC_RESTART_DONE is set when we + * restart a handshake because of MS SGC and so prevents us +diff -up openssl-1.0.1e/ssl/s3_clnt.c.keying-mitm openssl-1.0.1e/ssl/s3_clnt.c +--- openssl-1.0.1e/ssl/s3_clnt.c.keying-mitm 2013-02-11 16:26:04.000000000 +0100 ++++ openssl-1.0.1e/ssl/s3_clnt.c 2014-06-02 19:49:57.042701985 +0200 +@@ -559,6 +559,7 @@ int ssl3_connect(SSL *s) + case SSL3_ST_CR_FINISHED_A: + case SSL3_ST_CR_FINISHED_B: + ++ s->s3->flags |= SSL3_FLAGS_CCS_OK; + ret=ssl3_get_finished(s,SSL3_ST_CR_FINISHED_A, + SSL3_ST_CR_FINISHED_B); + if (ret <= 0) goto end; +@@ -916,6 +917,7 @@ int ssl3_get_server_hello(SSL *s) + SSLerr(SSL_F_SSL3_GET_SERVER_HELLO,SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT); + goto f_err; + } ++ s->s3->flags |= SSL3_FLAGS_CCS_OK; + s->hit=1; + } + else /* a miss or crap from the other end */ +diff -up openssl-1.0.1e/ssl/s3_pkt.c.keying-mitm openssl-1.0.1e/ssl/s3_pkt.c +--- openssl-1.0.1e/ssl/s3_pkt.c.keying-mitm 2014-06-02 19:48:04.640103383 +0200 ++++ openssl-1.0.1e/ssl/s3_pkt.c 2014-06-02 19:48:04.643103452 +0200 +@@ -1298,6 +1298,15 @@ start: + goto f_err; + } + ++ if (!(s->s3->flags & SSL3_FLAGS_CCS_OK)) ++ { ++ al=SSL_AD_UNEXPECTED_MESSAGE; ++ SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_CCS_RECEIVED_EARLY); ++ goto f_err; ++ } ++ ++ s->s3->flags &= ~SSL3_FLAGS_CCS_OK; ++ + rr->length=0; + + if (s->msg_callback) +@@ -1432,7 +1441,7 @@ int ssl3_do_change_cipher_spec(SSL *s) + + if (s->s3->tmp.key_block == NULL) + { +- if (s->session == NULL) ++ if (s->session == NULL || s->session->master_key_length == 0) + { + /* might happen if dtls1_read_bytes() calls this */ + SSLerr(SSL_F_SSL3_DO_CHANGE_CIPHER_SPEC,SSL_R_CCS_RECEIVED_EARLY); +diff -up openssl-1.0.1e/ssl/s3_srvr.c.keying-mitm openssl-1.0.1e/ssl/s3_srvr.c +--- openssl-1.0.1e/ssl/s3_srvr.c.keying-mitm 2014-06-02 19:48:04.630103151 +0200 ++++ openssl-1.0.1e/ssl/s3_srvr.c 2014-06-02 19:48:04.643103452 +0200 +@@ -673,6 +673,7 @@ int ssl3_accept(SSL *s) + case SSL3_ST_SR_CERT_VRFY_A: + case SSL3_ST_SR_CERT_VRFY_B: + ++ s->s3->flags |= SSL3_FLAGS_CCS_OK; + /* we should decide if we expected this one */ + ret=ssl3_get_cert_verify(s); + if (ret <= 0) goto end; +@@ -700,6 +701,7 @@ int ssl3_accept(SSL *s) + + case SSL3_ST_SR_FINISHED_A: + case SSL3_ST_SR_FINISHED_B: ++ s->s3->flags |= SSL3_FLAGS_CCS_OK; + ret=ssl3_get_finished(s,SSL3_ST_SR_FINISHED_A, + SSL3_ST_SR_FINISHED_B); + if (ret <= 0) goto end; +@@ -770,7 +772,10 @@ int ssl3_accept(SSL *s) + s->s3->tmp.next_state=SSL3_ST_SR_FINISHED_A; + #else + if (s->s3->next_proto_neg_seen) ++ { ++ s->s3->flags |= SSL3_FLAGS_CCS_OK; + s->s3->tmp.next_state=SSL3_ST_SR_NEXT_PROTO_A; ++ } + else + s->s3->tmp.next_state=SSL3_ST_SR_FINISHED_A; + #endif diff --git a/device-software/meta-edison-distro/recipes-connectivity/openssl/openssl/openssl-1.0.1e-cve-2014-3470.patch b/device-software/meta-edison-distro/recipes-connectivity/openssl/openssl/openssl-1.0.1e-cve-2014-3470.patch new file mode 100644 index 0000000..025727f --- /dev/null +++ b/device-software/meta-edison-distro/recipes-connectivity/openssl/openssl/openssl-1.0.1e-cve-2014-3470.patch @@ -0,0 +1,31 @@ +commit 4ad43d511f6cf064c66eb4bfd0fb0919b5dd8a86 +Author: Dr. Stephen Henson +Date: Thu May 29 15:00:05 2014 +0100 + + Fix CVE-2014-3470 + + Check session_cert is not NULL before dereferencing it. + +Patch borrowed from Fedora +Upstream-Status: Backport +Signed-off-by: Paul Eggleton + + +diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c +index d35376d..4324f8d 100644 +--- a/ssl/s3_clnt.c ++++ b/ssl/s3_clnt.c +@@ -2511,6 +2511,13 @@ int ssl3_send_client_key_exchange(SSL *s) + int ecdh_clnt_cert = 0; + int field_size = 0; + ++ if (s->session->sess_cert == NULL) ++ { ++ ssl3_send_alert(s,SSL3_AL_FATAL,SSL_AD_UNEXPECTED_MESSAGE); ++ SSLerr(SSL_F_SSL3_SEND_CLIENT_KEY_EXCHANGE,SSL_R_UNEXPECTED_MESSAGE); ++ goto err; ++ } ++ + /* Did we send out the client's + * ECDH share for use in premaster + * computation as part of client certificate? diff --git a/device-software/meta-edison-distro/recipes-connectivity/openssl/openssl_1.0.1j.bb b/device-software/meta-edison-distro/recipes-connectivity/openssl/openssl_1.0.1j.bb new file mode 100644 index 0000000..ecb31d0 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-connectivity/openssl/openssl_1.0.1j.bb @@ -0,0 +1,54 @@ +require openssl.inc + +# For target side versions of openssl enable support for OCF Linux driver +# if they are available. +DEPENDS += "cryptodev-linux" + +CFLAG += "-DHAVE_CRYPTODEV -DUSE_CRYPTODEV_DIGESTS" + +LIC_FILES_CHKSUM = "file://LICENSE;md5=f9a8f968107345e0b75aa8c2ecaa7ec8" + +export DIRS = "crypto ssl apps engines" +export OE_LDFLAGS="${LDFLAGS}" + +SRC_URI += "file://configure-targets.patch \ + file://shared-libs.patch \ + file://oe-ldflags.patch \ + file://engines-install-in-libdir-ssl.patch \ + file://openssl-fix-link.patch \ + file://debian/version-script.patch \ + file://debian/pic.patch \ + file://debian/c_rehash-compat.patch \ + file://debian/ca.patch \ + file://debian/make-targets.patch \ + file://debian/no-rpath.patch \ + file://debian/man-dir.patch \ + file://debian/man-section.patch \ + file://debian/no-symbolic.patch \ + file://debian/debian-targets.patch \ + file://openssl_fix_for_x32.patch \ + file://fix-cipher-des-ede3-cfb1.patch \ + file://openssl-avoid-NULL-pointer-dereference-in-EVP_DigestInit_ex.patch \ + file://openssl-avoid-NULL-pointer-dereference-in-dh_pub_encode.patch \ + file://initial-aarch64-bits.patch \ + file://find.pl \ + file://openssl-fix-des.pod-error.patch \ + " + +SRC_URI[md5sum] = "f7175c9cd3c39bb1907ac8bba9df8ed3" +SRC_URI[sha256sum] = "1b60ca8789ba6f03e8ef20da2293b8dc131c39d83814e775069f02d26354edf3" + +PACKAGES =+ " \ + ${PN}-engines \ + ${PN}-engines-dbg \ + " + +FILES_${PN}-engines = "${libdir}/ssl/engines/*.so ${libdir}/engines" +FILES_${PN}-engines-dbg = "${libdir}/ssl/engines/.debug" + +PARALLEL_MAKE = "" +PARALLEL_MAKEINST = "" + +do_configure_prepend() { + cp ${WORKDIR}/find.pl ${S}/util/find.pl +} diff --git a/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/defconfig-gnutls b/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/defconfig-gnutls index 4f0b510..71f42e6 100644 --- a/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/defconfig-gnutls +++ b/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/defconfig-gnutls @@ -420,6 +420,10 @@ CONFIG_CTRL_IFACE_DBUS_NEW=y # external RADIUS server can be supported with hostapd. CONFIG_AP=y +# P2P (Wi-Fi Direct) +# This can be used to enable P2P support in wpa_supplicant. See README-P2P for +# more information on P2P operations. +CONFIG_P2P=y CONFIG_BGSCAN_SIMPLE=y # Autoscan @@ -432,8 +436,5 @@ CONFIG_AUTOSCAN_EXPONENTIAL=y # For periodic module: #CONFIG_AUTOSCAN_PERIODIC=y -# WPA_CLI Private commands -# This enables private commands usage by wpa_cli -# -CFLAGS += -DEDISON_USE_PRIVATE_BCM_CMD -CONFIG_USE_VENDOR_PRIVATE_CMD=y +CFLAGS += -DEDISON_TARGET +CFLAGS += -DANDROID_P2P diff --git a/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/p2p_supplicant.conf-sane b/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/p2p_supplicant.conf-sane new file mode 100644 index 0000000..62e2236 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/p2p_supplicant.conf-sane @@ -0,0 +1,12 @@ +ctrl_interface=/var/run/wpa_supplicant +ctrl_interface_group=0 +config_methods=virtual_push_button virtual_display push_button keypad +update_config=1 +fast_reauth=1 +device_name=Edison +manufacturer=Intel +model_name=Edison +p2p_ssid_postfix=-Edison +persistent_reconnect=1 + + diff --git a/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/udhcpd-p2p.conf b/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/udhcpd-p2p.conf new file mode 100644 index 0000000..dcd92f2 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/udhcpd-p2p.conf @@ -0,0 +1,107 @@ +# Sample udhcpd configuration file (/etc/udhcpd.conf) + +# The start and end of the IP lease block + +start 192.168.42.20 #default: 192.168.0.20 +end 192.168.42.254 #default: 192.168.0.254 + + +# The interface that udhcpd will use + +interface INTERFACE #default: eth0 + + +# The maximim number of leases (includes addressesd reserved +# by OFFER's, DECLINE's, and ARP conficts + +#max_leases 254 #default: 254 + + +# If remaining is true (default), udhcpd will store the time +# remaining for each lease in the udhcpd leases file. This is +# for embedded systems that cannot keep time between reboots. +# If you set remaining to no, the absolute time that the lease +# expires at will be stored in the dhcpd.leases file. + +#remaining yes #default: yes + + +# The time period at which udhcpd will write out a dhcpd.leases +# file. If this is 0, udhcpd will never automatically write a +# lease file. (specified in seconds) + +#auto_time 7200 #default: 7200 (2 hours) + + +# The amount of time that an IP will be reserved (leased) for if a +# DHCP decline message is received (seconds). + +#decline_time 3600 #default: 3600 (1 hour) + + +# The amount of time that an IP will be reserved (leased) for if an +# ARP conflct occurs. (seconds + +#conflict_time 3600 #default: 3600 (1 hour) + + +# How long an offered address is reserved (leased) in seconds + +#offer_time 60 #default: 60 (1 minute) + +# If a lease to be given is below this value, the full lease time is +# instead used (seconds). + +#min_lease 60 #defult: 60 + + +# The location of the leases file + +#lease_file /var/lib/misc/udhcpd.leases #defualt: /var/lib/misc/udhcpd.leases + +# The location of the pid file +pidfile /var/run/udhcpd-INTERFACE.pid #default: /var/run/udhcpd.pid + +# Every time udhcpd writes a leases file, the below script will be called. +# Useful for writing the lease file to flash every few hours. + +#notify_file #default: (no script) + +#notify_file dumpleases # <--- useful for debugging + +# The following are bootp specific options, setable by udhcpd. + +#siaddr 192.168.0.22 #default: 0.0.0.0 + +#sname zorak #default: (none) + +#boot_file /var/nfs_root #default: (none) + +# The remainer of options are DHCP options and can be specifed with the +# keyword 'opt' or 'option'. If an option can take multiple items, such +# as the dns option, they can be listed on the same line, or multiple +# lines. The only option with a default is 'lease'. + +# Currently supported options, for more info, see options.c +opt subnet 255.255.255.0 +#opt timezone +#opt router +#opt timesvr +#opt namesvr +#opt dns +#opt logsvr +#opt cookiesvr +#opt lprsvr +#opt bootsize +#opt domain +#opt swapsvr +#opt rootpath +#opt ipttl +#opt mtu +#opt broadcast +#opt wins +#opt lease +#opt ntpsrv +#opt tftp +#opt bootfile + diff --git a/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/wpa-supplicant-android-4.4.4_r2.0.1.patch b/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/wpa-supplicant-android-4.4.4_r2.0.1.patch new file mode 100644 index 0000000..ceee40d --- /dev/null +++ b/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/wpa-supplicant-android-4.4.4_r2.0.1.patch @@ -0,0 +1,184 @@ + +--- a/wpa_supplicant/src/drivers/driver_nl80211.c 2014-10-01 16:44:14.819137037 +0200 ++++ b/wpa_supplicant/src/drivers/driver_nl80211.c 2014-10-01 16:37:16.979149254 +0200 +@@ -321,7 +321,7 @@ + struct wpa_driver_scan_params *params); + static int android_pno_stop(struct i802_bss *bss); + #endif /* ANDROID */ +-#ifdef ANDROID_P2P ++#if defined(ANDROID_P2P) && !defined(EDISON_TARGET) + int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration); + int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len); + int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow); +@@ -10329,7 +10329,7 @@ + "opp_ps=%d ctwindow=%d)", legacy_ps, opp_ps, ctwindow); + + if (opp_ps != -1 || ctwindow != -1) +-#ifdef ANDROID_P2P ++#if defined(ANDROID_P2P) && !defined(EDISON_TARGET) + wpa_driver_set_p2p_ps(priv, legacy_ps, opp_ps, ctwindow); + #else + return -1; /* Not yet supported */ +@@ -10832,7 +10832,7 @@ + .update_ft_ies = wpa_driver_nl80211_update_ft_ies, + .get_mac_addr = wpa_driver_nl80211_get_macaddr, + .get_survey = wpa_driver_nl80211_get_survey, +-#ifdef ANDROID_P2P ++#if defined(ANDROID_P2P) && !defined(EDISON_TARGET) + .set_noa = wpa_driver_set_p2p_noa, + .get_noa = wpa_driver_get_p2p_noa, + .set_ap_wps_ie = wpa_driver_set_ap_wps_p2p_ie, + +--- a/wpa_supplicant/wpa_cli.c 2014-10-01 16:52:31.123122526 +0200 ++++ b/wpa_supplicant/wpa_cli.c 2014-10-01 17:05:59.043098904 +0200 +@@ -3625,7 +3625,8 @@ + continue; + #endif /* _DIRENT_HAVE_D_TYPE */ + if (os_strcmp(dent->d_name, ".") == 0 || +- os_strcmp(dent->d_name, "..") == 0) ++ os_strcmp(dent->d_name, "..") == 0 || ++ os_strstr(dent->d_name, "p2p")) + continue; + printf("Selected interface '%s'\n", dent->d_name); + ifname = os_strdup(dent->d_name); + +--- a/wpa_supplicant/p2p_supplicant.c 2014-09-26 12:09:39.856249682 +0200 ++++ b/wpa_supplicant/p2p_supplicant.c 2014-12-17 16:42:39.535846212 +0100 +@@ -3061,6 +3061,13 @@ + int cla, op; + + if (wpa_s->hw.modes == NULL) { ++ wpa_printf(MSG_DEBUG, "P2P: Driver did not support fetching. " ++ "Use supported channels retrieved on interface wlan0"); ++ wpa_s = wpa_s->global->ifaces; ++ ++ } ++ ++ if (wpa_s->hw.modes == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Driver did not support fetching " + "of all supported channels; assume dualband " + "support"); +@@ -3172,7 +3179,8 @@ + } + + +-int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s) ++int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s, ++ const char *conf_p2p_dev) + { + struct wpa_interface iface; + struct wpa_supplicant *p2pdev_wpa_s; +@@ -3198,7 +3206,20 @@ + iface.ifname = wpa_s->pending_interface_name; + iface.driver = wpa_s->driver->name; + iface.driver_param = wpa_s->conf->driver_param; +- iface.confname = wpa_s->confname; ++ ++ /* ++ * If a P2P Device configuration file was given, use it as the interface ++ * configuration file (instead of using parent's configuration file. ++ */ ++ if (conf_p2p_dev) { ++ iface.confname = conf_p2p_dev; ++ iface.ctrl_interface = NULL; ++ } else { ++ iface.confname = wpa_s->confname; ++ iface.ctrl_interface = wpa_s->conf->ctrl_interface; ++ } ++ iface.conf_p2p_dev = NULL; ++ + p2pdev_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface); + if (!p2pdev_wpa_s) { + wpa_printf(MSG_DEBUG, "P2P: Failed to add P2P Device interface"); + +--- a/wpa_supplicant/main.c 2014-09-26 12:09:39.856249682 +0200 ++++ b/wpa_supplicant/main.c 2014-12-17 16:42:39.535846212 +0100 +@@ -83,6 +83,9 @@ + #endif /* CONFIG_DBUS */ + printf(" -v = show version\n" + " -W = wait for a control interface monitor before starting\n" ++#ifdef CONFIG_P2P ++ " -m = Configuration file for the P2P Device interface\n" ++#endif /* CONFIG_P2P */ + " -N = start describing new interface\n"); + + printf("example:\n" +@@ -160,7 +163,7 @@ + + for (;;) { + c = getopt(argc, argv, +- "b:Bc:C:D:de:f:g:G:hi:I:KLNo:O:p:P:qsTtuvW"); ++ "b:Bc:C:D:de:f:g:G:hi:I:KLm:No:O:p:P:qsTtuvW"); + if (c < 0) + break; + switch (c) { +@@ -220,6 +223,11 @@ + license(); + exitcode = 0; + goto out; ++#ifdef CONFIG_P2P ++ case 'm': ++ iface->conf_p2p_dev = optarg; ++ break; ++#endif /* CONFIG_P2P */ + case 'o': + params.override_driver = optarg; + break; +@@ -311,7 +319,7 @@ + if (wpa_s->global->p2p == NULL && + (wpa_s->drv_flags & + WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) && +- wpas_p2p_add_p2pdev_interface(wpa_s) < 0) ++ wpas_p2p_add_p2pdev_interface(wpa_s, iface->conf_p2p_dev) < 0) + exitcode = -1; + #endif /* CONFIG_P2P */ + } + +--- a/wpa_supplicant/p2p_supplicant.h 2014-09-26 12:09:39.856249682 +0200 ++++ b/wpa_supplicant/p2p_supplicant.h 2014-12-17 16:42:39.535846212 +0100 +@@ -19,7 +19,8 @@ + int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s); + void wpas_p2p_deinit(struct wpa_supplicant *wpa_s); + void wpas_p2p_deinit_global(struct wpa_global *global); +-int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s); ++int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s, ++ const char *conf_p2p_dev); + int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, + const char *pin, enum p2p_wps_method wps_method, + int persistent_group, int auto_join, int join, + +--- a/wpa_supplicant/wpa_supplicant_i.h 2014-09-26 12:09:39.856249682 +0200 ++++ b/wpa_supplicant/wpa_supplicant_i.h 2014-12-17 16:42:39.535846212 +0100 +@@ -63,6 +63,17 @@ + */ + const char *confanother; + ++#ifdef CONFIG_P2P ++ /** ++ * conf_p2p_dev - Configuration file used to hold the ++ * P2P Device configuration parameters. ++ * ++ * This can also be %NULL. In such a case, if a P2P Device dedicated ++ * interfaces is created, the main configuration file will be used. ++ */ ++ const char *conf_p2p_dev; ++#endif /* CONFIG_P2P */ ++ + /** + * ctrl_interface - Control interface parameter + * + +@@ -904,0 +904,0 @@ +--- a/wpa_supplicant/scan.c 2014-09-26 12:09:39.856249682 +0200 ++++ b/wpa_supplicant/scan.c 2014-12-17 16:42:39.535846212 +0100 +@@ -904,7 +904,7 @@ + */ + void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec) + { +-#ifndef ANDROID ++#if !defined(ANDROID) && !defined(EDISON_TARGET) + /* If there's at least one network that should be specifically scanned + * then don't cancel the scan and reschedule. Some drivers do + * background scanning which generates frequent scan results, and that + + diff --git a/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/wpa_cli-actions.sh b/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/wpa_cli-actions.sh index b4bd8d8..77b9086 100644 --- a/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/wpa_cli-actions.sh +++ b/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/wpa_cli-actions.sh @@ -3,8 +3,10 @@ # This script file is passed as parameter to wpa_cli, started as a daemon, # so that the wpa_supplicant events are sent to this script # and actions executed, like : -# - start DHCP client when Wifi is connected. -# - stop DHCP client when Wifi is disconnected. +# - start DHCP client when STA is connected. +# - stop DHCP client when STA is disconnected. +# - start DHCP client when P2P-GC is connected. +# - stop DHCP server when P2P-GO is disconnected. # # This script skips events if connmand (connman.service) is started # Indeed, it is considered that the Wifi connection is managed through @@ -36,11 +38,14 @@ kill_daemon() { echo "event $CMD received from wpa_supplicant" -# if connman is started, ignore the connmand. -# DHCP connection is triggerd by connman +# if Connman is started, ignore wpa_supplicant +# STA connection event because the DHCP connection +# is triggerd by Connman if [ `systemctl is-active connman` == "active" ] ; then - echo "event $CMD ignored because is started" - exit 0 + if [ "$CMD" = "CONNECTED" ] || [ "$CMD" = "DISCONNECTED" ] ; then + echo "event $CMD ignored because Connman is started" + exit 0 + fi fi if [ "$CMD" = "CONNECTED" ]; then @@ -53,3 +58,31 @@ if [ "$CMD" = "DISCONNECTED" ]; then ifconfig $IFNAME 0.0.0.0 fi +if [ "$CMD" = "P2P-GROUP-STARTED" ]; then + GIFNAME=$3 + if [ "$4" = "GO" ]; then + kill_daemon udhcpc /var/run/udhcpc-$GIFNAME.pid + ifconfig $GIFNAME 192.168.42.1 up + cp /etc/wpa_supplicant/udhcpd-p2p.conf /etc/wpa_supplicant/udhcpd-p2p-itf.conf + sed -i "s/INTERFACE/$GIFNAME/" /etc/wpa_supplicant/udhcpd-p2p-itf.conf + udhcpd /etc/wpa_supplicant/udhcpd-p2p-itf.conf + fi + if [ "$4" = "client" ]; then + kill_daemon udhcpc /var/run/udhcpc-$GIFNAME.pid + kill_daemon udhcpd /var/run/udhcpd-$GIFNAME.pid + udhcpc -i $GIFNAME -p /var/run/udhcpc-$GIFNAME.pid + fi +fi + +if [ "$CMD" = "P2P-GROUP-REMOVED" ]; then + GIFNAME=$3 + if [ "$4" = "GO" ]; then + kill_daemon udhcpd /var/run/udhcpd-$GIFNAME.pid + ifconfig $GIFNAME 0.0.0.0 + fi + if [ "$4" = "client" ]; then + kill_daemon udhcpc /var/run/udhcpc-$GIFNAME.pid + ifconfig $GIFNAME 0.0.0.0 + fi +fi + diff --git a/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/wpa_supplicant.conf-sane b/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/wpa_supplicant.conf-sane index e737c3c..4d37989 100644 --- a/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/wpa_supplicant.conf-sane +++ b/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/wpa_supplicant.conf-sane @@ -1,5 +1,10 @@ ctrl_interface=/var/run/wpa_supplicant ctrl_interface_group=0 +config_methods=virtual_push_button virtual_display push_button keypad update_config=1 -ap_scan=1 +fast_reauth=1 +device_name=Edison +manufacturer=Intel +model_name=Edison + diff --git a/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/wpa_supplicant.service b/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/wpa_supplicant.service index ce95f18..ec363aa 100644 --- a/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/wpa_supplicant.service +++ b/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/wpa_supplicant.service @@ -6,14 +6,16 @@ After=sys-subsystem-net-devices-wlan0.device [Service] Type=simple -# start wpa_supplicant service -ExecStart=/usr/sbin/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant.conf -iwlan0 -Dnl80211 -u -O /var/run/wpa_supplicant +# start wpa_supplicant service : +# - the second interface p2p-dev-wlan0 is automatically created at startup +# - the file wpa_supplicant is common to both interfaces wlan0 and p2p-dev-wlan0 +ExecStart=/usr/sbin/wpa_supplicant -u -c/etc/wpa_supplicant/wpa_supplicant.conf -iwlan0 -Dnl80211 -puse_p2p_group_interface=1p2p_device=1 -m/etc/wpa_supplicant/p2p_supplicant.conf -O /var/run/wpa_supplicant -e /etc/wpa_supplicant/entropy.bin # start wpa_supplicant_event service after the start of wpa_supplicant service -ExecStartPost=/bin/systemctl start wpa_supplicant_event +ExecStartPost=/bin/systemctl start wpa_supplicant_wlan0_event ; /bin/systemctl start wpa_supplicant_p2p_event # stop wpa_supplicant_event service after the stop of wpa_supplicant service -ExecStopPost=/bin/systemctl stop wpa_supplicant_event +ExecStopPost=/bin/systemctl stop wpa_supplicant_wlan0_event ; /bin/systemctl stop wpa_supplicant_p2p_event [Install] WantedBy=multi-user.target diff --git a/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/wpa_supplicant_p2p_event.service b/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/wpa_supplicant_p2p_event.service new file mode 100644 index 0000000..b5445a2 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/wpa_supplicant_p2p_event.service @@ -0,0 +1,13 @@ +[Unit] +Description=Daemon to receive the wpa_supplicant event +Requires=sys-subsystem-net-devices-wlan0.device +After=sys-subsystem-net-devices-wlan0.device + +[Service] +Type=simple +ExecStart=/usr/sbin/wpa_cli -a /etc/wpa_supplicant/wpa_cli-actions.sh -i p2p-dev-wlan0 +Restart=on-failure +RestartSec=1 + +[Install] +WantedBy=multi-user.target diff --git a/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/wpa_supplicant_wlan0_event.service b/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/wpa_supplicant_wlan0_event.service new file mode 100644 index 0000000..7428b4c --- /dev/null +++ b/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant/wpa_supplicant_wlan0_event.service @@ -0,0 +1,13 @@ +[Unit] +Description=Daemon to receive the wpa_supplicant event +Requires=sys-subsystem-net-devices-wlan0.device +After=sys-subsystem-net-devices-wlan0.device + +[Service] +Type=simple +ExecStart=/usr/sbin/wpa_cli -a /etc/wpa_supplicant/wpa_cli-actions.sh -i wlan0 +Restart=on-failure +RestartSec=1 + +[Install] +WantedBy=multi-user.target diff --git a/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant_2.1.bbappend b/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant_2.1.bbappend index ecbfd5c..5063afd 100644 --- a/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant_2.1.bbappend +++ b/device-software/meta-edison-distro/recipes-connectivity/wpa_supplicant/wpa-supplicant_2.1.bbappend @@ -1,33 +1,37 @@ LIC_FILES_CHKSUM = "file://COPYING;md5=ab87f20cd7e8c0d0a6539b34d3791d0e \ - file://README;md5=a07250b28e857455336bb59fc31cb845 \ - file://wpa_supplicant/wpa_supplicant.c;beginline=1;endline=12;md5=e8e021e30f3a6ab7c341b66b86626a5a" + file://README;md5=5c7cc1ea1a4d82b1cbe9a02fe92881b8 \ + file://wpa_supplicant/wpa_supplicant.c;beginline=1;endline=12;md5=cba4fa09fa364da845ca546f21008909" RRECOMMENDS_${PN} = "wpa-supplicant-passphrase wpa-supplicant-cli" -SYSTEMD_SERVICE_${PN} = "wpa_supplicant.service wpa_supplicant_event.service" + +SYSTEMD_SERVICE_${PN} = "wpa_supplicant.service wpa_supplicant_wlan0_event.service wpa_supplicant_p2p_event.service" FILESEXTRAPATHS_prepend := "${THISDIR}/wpa-supplicant:" +PV = "android-4.4.4_r2.0.1" + BASE_SRC_URI = "file://defconfig-gnutls \ - file://wpa_supplicant.conf \ file://wpa_supplicant.conf-sane \ + file://p2p_supplicant.conf-sane \ file://99_wpa_supplicant \ file://fi.w1.wpa_supplicant1.service \ + file://udhcpd-p2p.conf \ file://wpa_supplicant.service \ - file://wpa_supplicant_event.service \ + file://wpa_supplicant_wlan0_event.service \ + file://wpa_supplicant_p2p_event.service \ file://wpa-supplicant.sh \ + file://wpa-supplicant-${PV}.patch \ file://wpa_cli-actions.sh " -PV = "2.2" - SRC_URI = "${BASE_SRC_URI} \ - http://hostap.epitest.fi/releases/wpa_supplicant-${PV}.tar.gz " + git://android.googlesource.com/platform/external/wpa_supplicant_8;protocol=https;tag=android-4.4.4_r2.0.1" +S = "${WORKDIR}/git" PR = "r1" -SRC_URI[md5sum] = "238e8e888bbd558e1a57e3eb28d1dd07" -SRC_URI[sha256sum] = "e0d8b8fd68a659636eaba246bb2caacbf53d22d53b2b6b90eb4b4fef0993c8ed" - +SRC_URI[md5sum] = "f2ed8fef72cf63d8d446a2d0a6da630a" +SRC_URI[sha256sum] = "eaaa5bf3055270e521b2dff64f2d203ec8040f71958b8588269a82c00c9d7b6a" FILES_${PN} += "${datadir}/dbus-1/system-services/* \ ${systemd_unitdir}/system/ \ @@ -52,6 +56,7 @@ do_install () { install -d ${D}${sysconfdir}/wpa_supplicant install -m 600 ${WORKDIR}/wpa_supplicant.conf-sane ${D}${sysconfdir}/wpa_supplicant/wpa_supplicant.conf + install -m 600 ${WORKDIR}/p2p_supplicant.conf-sane ${D}${sysconfdir}/wpa_supplicant/p2p_supplicant.conf install -d ${D}/${sysconfdir}/dbus-1/system.d install -m 644 ${S}/wpa_supplicant/dbus/dbus-wpa_supplicant.conf ${D}/${sysconfdir}/dbus-1/system.d @@ -70,7 +75,11 @@ do_install () { # Install wpa_supplicant_event service for udhcp client start/stop based on wifi connection/disconnection install -m 755 ${WORKDIR}/wpa_cli-actions.sh ${D}${sysconfdir}/wpa_supplicant - install -m 644 ${WORKDIR}/wpa_supplicant_event.service ${D}${systemd_unitdir}/system + install -m 644 ${WORKDIR}/wpa_supplicant_wlan0_event.service ${D}${systemd_unitdir}/system + install -m 644 ${WORKDIR}/wpa_supplicant_p2p_event.service ${D}${systemd_unitdir}/system + + # Install udhcp server configuration file for P2P GO + install -m 644 ${WORKDIR}/udhcpd-p2p.conf ${D}${sysconfdir}/wpa_supplicant fi install -d ${D}/etc/default/volatiles diff --git a/device-software/meta-edison-distro/recipes-core/base-files/base-files/media-sdcard.automount b/device-software/meta-edison-distro/recipes-core/base-files/base-files/media-sdcard.automount new file mode 100644 index 0000000..a577591 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-core/base-files/base-files/media-sdcard.automount @@ -0,0 +1,11 @@ +[Unit] +Description=Automount for SDCard +ConditionPathExists=/dev/mmcblk1p1 +RefuseManualStart=true +RefuseManualStop=true + +[Automount] +Where=/media/sdcard + +[Install] +WantedBy=local-fs.target diff --git a/device-software/meta-edison-distro/recipes-core/base-files/base-files/media-sdcard.mount b/device-software/meta-edison-distro/recipes-core/base-files/base-files/media-sdcard.mount index c7d52ac..a9d310b 100644 --- a/device-software/meta-edison-distro/recipes-core/base-files/base-files/media-sdcard.mount +++ b/device-software/meta-edison-distro/recipes-core/base-files/base-files/media-sdcard.mount @@ -1,10 +1,7 @@ [Unit] Description=Mount for SDCard -Requires=dev-mmcblk1p1.device -After=dev-mmcblk1p1.device - -[Install] -WantedBy=default.target +BindsTo=dev-mmcblk1p1.device +Conflicts=media-sdcard.automount [Mount] What=/dev/mmcblk1p1 diff --git a/device-software/meta-edison-distro/recipes-core/base-files/base-files/share/dot.profile b/device-software/meta-edison-distro/recipes-core/base-files/base-files/share/dot.profile new file mode 100644 index 0000000..4f1d2a0 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-core/base-files/base-files/share/dot.profile @@ -0,0 +1,9 @@ +# ~/.profile: executed by Bourne-compatible login shells. + +if [ -f ~/.bashrc ]; then + . ~/.bashrc +fi + +# path set by /etc/profile +# export PATH + diff --git a/device-software/meta-edison-distro/recipes-core/base-files/base-files_3.0.14.bbappend b/device-software/meta-edison-distro/recipes-core/base-files/base-files_3.0.14.bbappend index 13e7606..dd8d96a 100644 --- a/device-software/meta-edison-distro/recipes-core/base-files/base-files_3.0.14.bbappend +++ b/device-software/meta-edison-distro/recipes-core/base-files/base-files_3.0.14.bbappend @@ -1,26 +1,33 @@ FILESEXTRAPATHS_prepend := "${THISDIR}/base-files:" SRC_URI += "file://fstab" SRC_URI += "file://media-sdcard.mount" +SRC_URI += "file://media-sdcard.automount" SRC_URI += "file://factory.mount" +SRC_URI += "file://share/dot.profile" # override default volatile to suppress var/log link creation volatiles = "tmp" do_install_append() { install -m 0644 ${WORKDIR}/fstab ${D}${sysconfdir}/fstab + install -m 0755 ${WORKDIR}/share/dot.profile ${D}${sysconfdir}/skel/.profile # enable mount of the SDCard in /media/sdcard when inserted install -d ${D}${systemd_unitdir}/system install -c -m 0644 ${WORKDIR}/media-sdcard.mount ${D}${systemd_unitdir}/system + install -c -m 0644 ${WORKDIR}/media-sdcard.automount ${D}${systemd_unitdir}/system install -c -m 0644 ${WORKDIR}/factory.mount ${D}${systemd_unitdir}/system # Enable the service + install -d ${D}${sysconfdir}/systemd/system/local-fs.target.wants + ln -sf ${systemd_unitdir}/system/media-sdcard.automount \ + ${D}${sysconfdir}/systemd/system/local-fs.target.wants/media-sdcard.automount install -d ${D}${sysconfdir}/systemd/system/default.target.wants - ln -sf ${systemd_unitdir}/system/media-sdcard.mount \ - ${D}${sysconfdir}/systemd/system/default.target.wants/media-sdcard.mount - ln -sf ${systemd_unitdir}/system/factory.mount \ + ln -sf ${systemd_unitdir}/system/factory.mount \ ${D}${sysconfdir}/systemd/system/default.target.wants/factory.mount } FILES_${PN} += "${base_libdir}/systemd/system/*.mount" +FILES_${PN} += "${base_libdir}/systemd/system/*.automount" FILES_${PN} += "${sysconfdir}/systemd/system/default.target.wants/*.mount" +FILES_${PN} += "${sysconfdir}/systemd/system/local-fs.target.wants/*.automount" diff --git a/device-software/meta-edison-distro/recipes-core/busybox/busybox_1.22.1.bbappend b/device-software/meta-edison-distro/recipes-core/busybox/busybox_1.22.1.bbappend index 7ce27a9..faf18af 100644 --- a/device-software/meta-edison-distro/recipes-core/busybox/busybox_1.22.1.bbappend +++ b/device-software/meta-edison-distro/recipes-core/busybox/busybox_1.22.1.bbappend @@ -1,4 +1,5 @@ -# to get brctl configuration settings -FILESEXTRAPATHS_prepend := "${THISDIR}/files/" +# to get brctl and log configuration settings +FILESEXTRAPATHS_prepend := "${THISDIR}/files/:" -SRC_URI += "file://brctl-utilities.cfg" +SRC_URI += "file://brctl-utilities.cfg \ + file://busybox-log.cfg " diff --git a/device-software/meta-edison-distro/recipes-core/busybox/files/busybox-log.cfg b/device-software/meta-edison-distro/recipes-core/busybox/files/busybox-log.cfg new file mode 100644 index 0000000..7b7408f --- /dev/null +++ b/device-software/meta-edison-distro/recipes-core/busybox/files/busybox-log.cfg @@ -0,0 +1,3 @@ +CONFIG_KLOGD=n +CONFIG_SYSLOGD=n + diff --git a/device-software/meta-edison-distro/recipes-core/first-install/files/first-install.service b/device-software/meta-edison-distro/recipes-core/first-install/files/first-install.service index 4c00f71..3ebb9d7 100644 --- a/device-software/meta-edison-distro/recipes-core/first-install/files/first-install.service +++ b/device-software/meta-edison-distro/recipes-core/first-install/files/first-install.service @@ -4,6 +4,6 @@ OnFailure=reboot.target Requires=dev-disk-by\x2dpartlabel-home.device dev-disk-by\x2dpartlabel-update.device After=dev-disk-by\x2dpartlabel-home.device dev-disk-by\x2dpartlabel-update.device [Service] -ExecStart=@BASE_BINDIR@/sh @BASE_SBINDIR@/first-install.sh systemd-service +ExecStart=@BASE_BINDIR@/sh -c "shell='sh'; if [ -f /bin/bash ]; then shell='bash'; fi; @BASE_BINDIR@/$shell @BASE_SBINDIR@/first-install.sh systemd-service" StandardOutput=journal+console diff --git a/device-software/meta-edison-distro/recipes-core/first-install/files/first-install.sh b/device-software/meta-edison-distro/recipes-core/first-install/files/first-install.sh index d429c8a..1b29245 100644 --- a/device-software/meta-edison-distro/recipes-core/first-install/files/first-install.sh +++ b/device-software/meta-edison-distro/recipes-core/first-install/files/first-install.sh @@ -1,9 +1,11 @@ -#!/bin/sh +#!/bin/bash # first install script to do post flash install # global variable set to 1 if output is systemd journal fi_journal_out=0 +export PATH="$PATH:/usr/sbin/" + # handle argument, if first-install is called from systemd service # arg1 is "systemd-service" if [ "$1" == "systemd-service" ]; then fi_journal_out=1; fi; @@ -48,11 +50,11 @@ exit_first_install () { # next reboot will be on multi-user target fw_setenv bootargs_target multi-user fi - - fi_echo "Rebooting...." # dump journal to log file journalctl -u first-install -o short-iso >> /first-install.log - reboot + systemctl daemon-reload + systemctl stop home.mount + systemctl default } # continue normal flow or exit on error code @@ -128,15 +130,17 @@ mkfs.ext4 -m0 /dev/disk/by-partlabel/home fi_assert $? "Formatting home partition" # backup initial /home/root directory -cp -R /home/root /tmp +mkdir /tmp/oldhome +cp -R /home/* /tmp/oldhome/ fi_assert $? "Backup home/root contents of rootfs" # mount home partition on /home mount /dev/disk/by-partlabel/home /home fi_assert $? "Mount /home partition" -# copy back contents to /home -mv /tmp/root /home +# copy back contents to /home and cleanup +mv /tmp/oldhome/* /home/ +rm -rf /tmp/oldhome fi_assert $? "Restore home/root contents on new /home partition" # create a fat32 primary partition on all available space diff --git a/device-software/meta-edison-distro/recipes-core/images/edison-image.bb b/device-software/meta-edison-distro/recipes-core/images/edison-image.bb index cea51da..f106657 100644 --- a/device-software/meta-edison-distro/recipes-core/images/edison-image.bb +++ b/device-software/meta-edison-distro/recipes-core/images/edison-image.bb @@ -49,7 +49,8 @@ IMAGE_INSTALL += "connman-tools" IMAGE_INSTALL += "wireless-tools" IMAGE_INSTALL += "wpa-supplicant" IMAGE_INSTALL += "hostapd-daemon" -IMAGE_INSTALL += "bluez5" +IMAGE_INSTALL += "bluez5-dev" +IMAGE_INSTALL += "bluez5-obex" IMAGE_INSTALL += "kernel-modules" IMAGE_INSTALL += "ethtool" IMAGE_INSTALL += "iptables" @@ -57,15 +58,15 @@ IMAGE_INSTALL += "libstdc++" IMAGE_INSTALL += "u-boot" IMAGE_INSTALL += "u-boot-fw-utils" IMAGE_INSTALL += "file" -IMAGE_INSTALL += "otg" IMAGE_INSTALL += "pciutils" IMAGE_INSTALL += "usbutils" IMAGE_INSTALL += "ldd" IMAGE_INSTALL += "i2c-tools" IMAGE_INSTALL += "watchdog-sample" IMAGE_INSTALL += "pwr-button-handler" -IMAGE_INSTALL += "libwebsockets" +IMAGE_INSTALL += "blink-led" IMAGE_INSTALL += "first-install" +IMAGE_INSTALL += "resize-rootfs" IMAGE_INSTALL += "systemd-analyze" IMAGE_INSTALL += "wget" IMAGE_INSTALL += "ota-update" @@ -73,10 +74,17 @@ IMAGE_INSTALL += "ota-update" # Allows to enable OpenMP feature IMAGE_INSTALL += "libgomp" +# Add audio firmware +IMAGE_INSTALL += "sst-fw-bin" + +# ALSA lib and utilities +IMAGE_INSTALL += "alsa-lib" +IMAGE_INSTALL += "alsa-utils-alsamixer alsa-utils-alsactl alsa-utils-aplay alsa-utils-amixer" + # Python and some basic modules IMAGE_INSTALL += "python" IMAGE_INSTALL += "python-dbus python-smartpm python-pygobject python-argparse" -IMAGE_INSTALL += "python-distutils python-pkgutil python-audio python-image python-imaging python-email python-netserver python-xmlrpc python-ctypes python-html python-json python-compile python-misc python-numbers python-unittest" +IMAGE_INSTALL += "python-distutils python-pkgutil python-audio python-image python-imaging python-email python-netserver python-xmlrpc python-ctypes python-html python-json python-compile python-misc python-numbers python-unittest python-pydoc" # Wifi firmware IMAGE_INSTALL += "bcm43340-fw" @@ -91,12 +99,21 @@ IMAGE_INSTALL += "bcm43340-mod" IMAGE_FEATURES += "tools-debug" IMAGE_INSTALL += "crashlog" +# Clean corrupted journald entries +IMAGE_INSTALL += "cleanjournal" + # Adds various other tools IMAGE_INSTALL += "tcpdump" IMAGE_INSTALL += "net-tools" IMAGE_INSTALL += "lsof" IMAGE_INSTALL += "iperf" +# Add pulseaudio +IMAGE_INSTALL += "pulseaudio-server libpulsecore libpulsecommon libpulse libpulse-simple pulseaudio-misc pulseaudio-service" + +# Add Mplayer +IMAGE_INSTALL += "mplayer" + # Those are necessary to manually create partitions and file systems on the eMMC IMAGE_INSTALL += "parted" IMAGE_INSTALL += "e2fsprogs-e2fsck e2fsprogs-mke2fs e2fsprogs-tune2fs e2fsprogs-badblocks libcomerr libss libe2p libext2fs dosfstools" @@ -106,3 +123,8 @@ IMAGE_INSTALL += "tzdata" # SWIG IMAGE_INSTALL += "swig" + +# INTEL MCU FW +IMAGE_INSTALL += "mcu-fw-load" +IMAGE_INSTALL += "mcu-fw-bin" + diff --git a/device-software/meta-edison-distro/recipes-core/ota-update/files/ota-update.sh b/device-software/meta-edison-distro/recipes-core/ota-update/files/ota-update.sh index 5d00b6d..cfbe8ea 100644 --- a/device-software/meta-edison-distro/recipes-core/ota-update/files/ota-update.sh +++ b/device-software/meta-edison-distro/recipes-core/ota-update/files/ota-update.sh @@ -54,7 +54,9 @@ exit_ota_update () { fi_echo "Rebooting...." # dump journal to log file journalctl -u ota-update -o short-iso >> /ota-update.log - reboot + systemctl daemon-reload + systemctl stop home.mount + systemctl default } # continue normal flow or exit on error code diff --git a/device-software/meta-edison-distro/recipes-core/readline/files/readline-fix-segfault-when-pressing-DEL-key-twice.patch b/device-software/meta-edison-distro/recipes-core/readline/files/readline-fix-segfault-when-pressing-DEL-key-twice.patch new file mode 100644 index 0000000..4dcaf89 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-core/readline/files/readline-fix-segfault-when-pressing-DEL-key-twice.patch @@ -0,0 +1,39 @@ +From 8acbcaa6b3d855f3e85e6a108db323278584091e Mon Sep 17 00:00:00 2001 +From: Loic Akue +Date: Mon, 3 Nov 2014 16:26:42 +0100 +Subject: [PATCH] bug readline + +| READLINE PATCH REPORT +| ===================== +| +|Readline-Release: 6.3 +|Patch-ID: readline63-002 +| +|Bug-Reported-by: Anatol Pomozov +|Bug-Reference-ID: +|Bug-Reference-URL: http://lists.gnu.org/archive/html/bug-readline/2014-03/msg00010.html +| +|Bug-Description: +| +|When in callback mode, some readline commands can cause readline to seg +|fault by passing invalid contexts to callback functions. + +Signed-off-by: Loic Akue + +diff --git a/readline.c b/readline.c +index 03eefa6..55c0522 100644 +--- a/readline.c ++++ b/readline.c +@@ -744,7 +744,8 @@ _rl_dispatch_callback (cxt) + r = _rl_subseq_result (r, cxt->oldmap, cxt->okey, (cxt->flags & KSEQ_SUBSEQ)); + + RL_CHECK_SIGNALS (); +- if (r == 0) /* success! */ ++ /* We only treat values < 0 specially to simulate recursion. */ ++ if (r >= 0 || (r == -1 && (cxt->flags & KSEQ_SUBSEQ) == 0)) /* success! or failure! */ + { + _rl_keyseq_chain_dispose (); + RL_UNSETSTATE (RL_STATE_MULTIKEY); +-- +1.7.9.5 + diff --git a/device-software/meta-edison-distro/recipes-core/readline/readline_6.3.bbappend b/device-software/meta-edison-distro/recipes-core/readline/readline_6.3.bbappend new file mode 100644 index 0000000..c8ecc36 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-core/readline/readline_6.3.bbappend @@ -0,0 +1,4 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" +SRC_URI += "\ + file://readline-fix-segfault-when-pressing-DEL-key-twice.patch \ + " diff --git a/device-software/meta-edison-distro/recipes-core/resize-rootfs/files/resize-rootfs.service b/device-software/meta-edison-distro/recipes-core/resize-rootfs/files/resize-rootfs.service new file mode 100644 index 0000000..708226f --- /dev/null +++ b/device-software/meta-edison-distro/recipes-core/resize-rootfs/files/resize-rootfs.service @@ -0,0 +1,14 @@ +[Unit] +Description=Increases rootfs image size to fit partition +Requires=dev-disk-by\x2dpartlabel-rootfs.device +After=dev-disk-by\x2dpartlabel-rootfs.device + +[Service] +Type=oneshot +ExecStart=/sbin/resize2fs /dev/disk/by-partlabel/rootfs +ExecStart=/bin/systemctl disable resize-rootfs.service + +StandardOutput=journal+console + +[Install] +WantedBy=default.target diff --git a/device-software/meta-edison-distro/recipes-core/resize-rootfs/resize-rootfs.bb b/device-software/meta-edison-distro/recipes-core/resize-rootfs/resize-rootfs.bb new file mode 100644 index 0000000..f91e4ba --- /dev/null +++ b/device-software/meta-edison-distro/recipes-core/resize-rootfs/resize-rootfs.bb @@ -0,0 +1,24 @@ +DESCRIPTION = "Resize Rootfs systemd service" +LICENSE = "GPLv2+" +LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/GPL-2.0;md5=801f80980d171dd6425610833a22dbe6" + +SRC_URI = "file://resize-rootfs.service" + +inherit systemd + +SYSTEMD_SERVICE_${PN} = "resize-rootfs.service" + +RDEPENDS_${PN} = "systemd e2fsprogs-resize2fs" + +do_install() { + install -d ${D}${systemd_unitdir}/system + install -c -m 0644 ${WORKDIR}/resize-rootfs.service ${D}${systemd_unitdir}/system +} + +FILES_${PN} = "${base_libdir}/systemd/system/resize-rootfs.service" + +# As this package is tied to systemd, only build it when we're also building systemd. +python () { + if not oe.utils.contains ('DISTRO_FEATURES', 'systemd', True, False, d): + raise bb.parse.SkipPackage("'systemd' not in DISTRO_FEATURES") +} diff --git a/device-software/meta-edison-distro/recipes-core/systemd/files/edison-machine-id.service b/device-software/meta-edison-distro/recipes-core/systemd/files/edison-machine-id.service new file mode 100644 index 0000000..3caa836 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-core/systemd/files/edison-machine-id.service @@ -0,0 +1,14 @@ +[Unit] +Description= Generate unique machine-id +After=systemd-remount-fs.service + +[Service] +Type=oneshot +ExecStartPre=/bin/umount /etc/machine-id +ExecStart=/bin/systemd-machine-id-setup +ExecStartPost=/bin/systemctl disable edison-machine-id +StandardOutput=journal+console + +[Install] +WantedBy=basic.target + diff --git a/device-software/meta-edison-distro/recipes-core/systemd/files/hsu-pm-runtime.service b/device-software/meta-edison-distro/recipes-core/systemd/files/hsu-pm-runtime.service index 1b9f277..e81b6ff 100644 --- a/device-software/meta-edison-distro/recipes-core/systemd/files/hsu-pm-runtime.service +++ b/device-software/meta-edison-distro/recipes-core/systemd/files/hsu-pm-runtime.service @@ -6,6 +6,7 @@ After=getty.target [Service] Type=oneshot ExecStart=/bin/sh -c 'echo auto > /sys/devices/pci0000:00/0000:00:04.3/power/control' +ExecStart=/bin/sh -c 'echo auto > /sys/devices/pci0000:00/0000:00:04.1/power/control' StandardOutput=null [Install] diff --git a/device-software/meta-edison-distro/recipes-core/systemd/files/usb0.network b/device-software/meta-edison-distro/recipes-core/systemd/files/usb0.network new file mode 100644 index 0000000..b7d1042 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-core/systemd/files/usb0.network @@ -0,0 +1,6 @@ +[Match] +Name=usb0 + +[Network] +Address=192.168.2.15/24 + diff --git a/device-software/meta-edison-distro/recipes-core/systemd/systemd_%.bbappend b/device-software/meta-edison-distro/recipes-core/systemd/systemd_%.bbappend index 23bcc0c..07b6cb2 100644 --- a/device-software/meta-edison-distro/recipes-core/systemd/systemd_%.bbappend +++ b/device-software/meta-edison-distro/recipes-core/systemd/systemd_%.bbappend @@ -4,13 +4,15 @@ SRC_URI += "file://journald.conf \ file://system.conf \ file://systemd-reboot-service.patch \ file://hsu-pm-runtime.service \ - file://spi-disable-pm-runtime.service" + file://usb0.network \ + file://edison-machine-id.service" do_install_append() { # Push the custom conf files on target install -m 0644 ${WORKDIR}/journald.conf ${D}${sysconfdir}/systemd install -m 0644 ${WORKDIR}/system.conf ${D}${sysconfdir}/systemd install -m 0644 ${WORKDIR}/timesyncd.conf ${D}${sysconfdir}/systemd + install -m 0644 ${WORKDIR}/usb0.network ${D}${sysconfdir}/systemd/network # enable timesyncd service install -d ${D}${sysconfdir}/systemd/system/sysinit.target.wants @@ -22,11 +24,11 @@ do_install_append() { install -m 0644 ${WORKDIR}/hsu-pm-runtime.service \ ${D}${systemd_unitdir}/system/hsu-pm-runtime.service - # enable a custom service to disable the ssp_spi driver's runtime pm - install -d ${D}${sysconfdir}/systemd/system/default.target.wants - install -m 0644 ${WORKDIR}/spi-disable-pm-runtime.service \ - ${D}${systemd_unitdir}/system/spi-disable-pm-runtime.service - ln -sf ${systemd_unitdir}/system/spi-disable-pm-runtime.service \ - ${D}${sysconfdir}/systemd/system/default.target.wants/spi-disable-pm-runtime.service + # enable a custom service to provide a persistant machine-id value to Edison + install -d ${D}${sysconfdir}/systemd/system/basic.target.wants + install -m 0644 ${WORKDIR}/edison-machine-id.service \ + ${D}${systemd_unitdir}/system/edison-machine-id.service + ln -sf ${systemd_unitdir}/system/edison-machine-id.service \ + ${D}${sysconfdir}/systemd/system/basic.target.wants/edison-machine-id.service } diff --git a/device-software/meta-edison-distro/recipes-devtools/e2fsprogs/e2fsprogs_%.bbappend b/device-software/meta-edison-distro/recipes-devtools/e2fsprogs/e2fsprogs_%.bbappend new file mode 100644 index 0000000..01a6d33 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-devtools/e2fsprogs/e2fsprogs_%.bbappend @@ -0,0 +1,11 @@ +# Add resize2fs tool + + +EXTRA_OECONF += "\ + --enable-resizer \ +" + +PACKAGES =+ "e2fsprogs-resize2fs" + +FILES_e2fsprogs-resize2fs = "${base_sbindir}/resize2fs" + diff --git a/device-software/meta-edison-distro/recipes-devtools/swig/swig.inc b/device-software/meta-edison-distro/recipes-devtools/swig/swig.inc new file mode 100644 index 0000000..5571a55 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-devtools/swig/swig.inc @@ -0,0 +1,63 @@ +DESCRIPTION = "SWIG - Simplified Wrapper and Interface Generator" +HOMEPAGE = "http://swig.sourceforge.net/" +LICENSE = "BSD & GPLv3" +LIC_FILES_CHKSUM = "file://LICENSE;md5=e7807a6282784a7dde4c846626b08fc6 \ + file://LICENSE-GPL;md5=d32239bcb673463ab874e80d47fae504 \ + file://LICENSE-UNIVERSITIES;md5=8ce9dcc8f7c994de4a408b205c72ba08" + +SECTION = "devel" + +DEPENDS = "libpcre python" + +SRC_URI = "${SOURCEFORGE_MIRROR}/${BPN}/${BPN}-${PV}.tar.gz" + +inherit autotools pythonnative + +EXTRA_OECONF = " \ + --with-python=${PYTHON} \ + --without-allegrocl \ + --without-android \ + --without-boost \ + --without-chicken \ + --without-clisp \ + --without-csharp \ + --without-d \ + --without-gcj \ + --without-go \ + --without-guile \ + --without-java \ + --without-lua \ + --without-mzscheme \ + --without-ocaml \ + --without-octave \ + --without-perl5 \ + --without-pike \ + --without-php \ + --without-python3 \ + --without-r \ + --without-ruby \ + --without-tcl \ +" + +BBCLASSEXTEND = "native nativesdk" + +do_configure() { + install -m 0755 ${STAGING_DATADIR_NATIVE}/gnu-config/config.guess ${S}/Tools/config + install -m 0755 ${STAGING_DATADIR_NATIVE}/gnu-config/config.sub ${S}/Tools/config + install -m 0755 ${STAGING_DATADIR_NATIVE}/gnu-config/config.guess ${S} + install -m 0755 ${STAGING_DATADIR_NATIVE}/gnu-config/config.sub ${S} + oe_runconf +} + +do_install_append_class-nativesdk() { + cd ${D}${bindir} + ln -s swig swig2.0 +} + +def swiglib_relpath(d): + swiglib = d.getVar('datadir', True) + "/" + d.getVar('BPN', True) + "/" + d.getVar('PV', True) + return os.path.relpath(swiglib, d.getVar('bindir', True)) + +do_install_append_class-native() { + create_wrapper ${D}${bindir}/swig SWIG_LIB='`dirname $''realpath`'/${@swiglib_relpath(d)} +} \ No newline at end of file diff --git a/device-software/meta-edison-distro/recipes-devtools/swig/swig/0001-Use-proc-self-exe-for-swig-swiglib-on-non-Win32-plat.patch b/device-software/meta-edison-distro/recipes-devtools/swig/swig/0001-Use-proc-self-exe-for-swig-swiglib-on-non-Win32-plat.patch new file mode 100644 index 0000000..4115469 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-devtools/swig/swig/0001-Use-proc-self-exe-for-swig-swiglib-on-non-Win32-plat.patch @@ -0,0 +1,56 @@ +--- + Source/Modules/main.cxx | 24 ++++++++++++++++++++++-- + 1 file changed, 22 insertions(+), 2 deletions(-) + +diff --git a/Source/Modules/main.cxx b/Source/Modules/main.cxx +index d2f5d3b..cbb0a12 100644 +--- a/Source/Modules/main.cxx ++++ b/Source/Modules/main.cxx +@@ -26,6 +26,11 @@ char cvsroot_main_cxx[] = "$Id$"; + #include "cparse.h" + #include + #include // for INT_MAX ++#ifndef _WIN32 ++#include ++#include // for readlink ++#include // for stat ++#endif + + // Global variables + +@@ -902,9 +907,9 @@ int SWIG_main(int argc, char *argv[], Language *l) { + + // Check for SWIG_LIB environment variable + if ((c = getenv("SWIG_LIB")) == (char *) 0) { ++ char *p; + #if defined(_WIN32) + char buf[MAX_PATH]; +- char *p; + if (!(GetModuleFileName(0, buf, MAX_PATH) == 0 || (p = strrchr(buf, '\\')) == 0)) { + *(p + 1) = '\0'; + SwigLib = NewStringf("%sLib", buf); // Native windows installation path +@@ -914,7 +919,22 @@ int SWIG_main(int argc, char *argv[], Language *l) { + if (Len(SWIG_LIB_WIN_UNIX) > 0) + SwigLibWinUnix = NewString(SWIG_LIB_WIN_UNIX); // Unix installation path using a drive letter (for msys/mingw) + #else +- SwigLib = NewString(SWIG_LIB); ++ char buf[PATH_MAX]; ++ if (0 < ::readlink("/proc/self/exe", buf, sizeof(buf)) && ++ (p = ::strstr(buf, "/bin/swig"))) { ++ int major, minor, patch; ++ const int ret = ::sscanf(VERSION, "%d.%d.%d", &major, &minor, &patch); ++ if (3 == ret) { ++ const ::ptrdiff_t dir_part_len = p - buf; ++ ::snprintf(p, PATH_MAX - dir_part_len, "/share/swig/%d.%d.%d", major, minor, patch); ++ struct ::stat stat_res; ++ if (0 == ::stat(buf, &stat_res) && S_ISDIR(stat_res.st_mode)) { ++ SwigLib = NewString(buf); ++ } ++ } ++ } ++ if (NULL == SwigLib) ++ SwigLib = NewString(SWIG_LIB); + #endif + } else { + SwigLib = NewString(c); +-- diff --git a/device-software/meta-edison-distro/recipes-devtools/swig/swig/0001-configure-use-pkg-config-for-pcre-detection.patch b/device-software/meta-edison-distro/recipes-devtools/swig/swig/0001-configure-use-pkg-config-for-pcre-detection.patch new file mode 100644 index 0000000..5a05d7b --- /dev/null +++ b/device-software/meta-edison-distro/recipes-devtools/swig/swig/0001-configure-use-pkg-config-for-pcre-detection.patch @@ -0,0 +1,55 @@ +--- + configure.ac | 38 +++++++------------------------------- + 1 file changed, 7 insertions(+), 31 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 0c984b7..6edcec1 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -70,38 +70,14 @@ AC_MSG_RESULT([$with_pcre]) + + dnl To make configuring easier, check for a locally built PCRE using the Tools/pcre-build.sh script + if test x"${with_pcre}" = xyes ; then +- AC_MSG_CHECKING([whether to use local PCRE]) +- local_pcre_config=no +- if test -z $PCRE_CONFIG; then +- if test -f `pwd`/pcre/pcre-swig-install/bin/pcre-config; then +- PCRE_CONFIG=`pwd`/pcre/pcre-swig-install/bin/pcre-config +- local_pcre_config=$PCRE_CONFIG +- fi +- fi +- AC_MSG_RESULT([$local_pcre_config]) +-fi +-AS_IF([test "x$with_pcre" != xno], +- [AX_PATH_GENERIC([pcre], +- [], dnl Minimal version of PCRE we need -- accept any +- [], dnl custom sed script for version parsing is not needed +- [AC_DEFINE([HAVE_PCRE], [1], [Define if you have PCRE library]) +- LIBS="$LIBS $PCRE_LIBS" +- CPPFLAGS="$CPPFLAGS $PCRE_CFLAGS" +- ], +- [AC_MSG_FAILURE([ +- Cannot find pcre-config script from PCRE (Perl Compatible Regular Expressions) +- library package. This dependency is needed for configure to complete, +- Either: +- - Install the PCRE developer package on your system (preferred approach). +- - Download the PCRE source tarball, build and install on your system +- as you would for any package built from source distribution. +- - Use the Tools/pcre-build.sh script to build PCRE just for SWIG to statically +- link against. Run 'Tools/pcre-build.sh --help' for instructions. +- (quite easy and does not require privileges to install PCRE on your system) +- - Use configure --without-pcre to disable regular expressions support in SWIG +- (not recommended).]) +- ]) ++ PKG_CHECK_MODULES([PCRE], [libpcre], [ ++ AC_DEFINE([HAVE_PCRE], [1], [Define if you have PCRE library]) ++ LIBS="$LIBS $PCRE_LIBS" ++ CPPFLAGS="$CPPFLAGS $PCRE_CFLAGS" ++ ], [ ++ AC_MSG_WARN([$PCRE_PKG_ERRORS]) + ]) ++fi + + + dnl CCache +-- diff --git a/device-software/meta-edison-distro/recipes-devtools/swig/swig_3.0.2.bb b/device-software/meta-edison-distro/recipes-devtools/swig/swig_3.0.2.bb new file mode 100644 index 0000000..ac41914 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-devtools/swig/swig_3.0.2.bb @@ -0,0 +1,8 @@ +require ${BPN}.inc + +SRC_URI += "file://0001-Use-proc-self-exe-for-swig-swiglib-on-non-Win32-plat.patch \ + file://0001-configure-use-pkg-config-for-pcre-detection.patch \ + " + +SRC_URI[md5sum] = "62f9b0d010cef36a13a010dc530d0d41" +SRC_URI[sha256sum] = "a2669657cabcedc371f63c0457407a183e0b6b2ef4e7e303c1ec9a3964cc7813" diff --git a/device-software/meta-edison-distro/recipes-multimedia/alsa/alsa-utils_%.bbappend b/device-software/meta-edison-distro/recipes-multimedia/alsa/alsa-utils_%.bbappend new file mode 100644 index 0000000..ae82a2b --- /dev/null +++ b/device-software/meta-edison-distro/recipes-multimedia/alsa/alsa-utils_%.bbappend @@ -0,0 +1,13 @@ +# Provide default alsa configuration for Edison + +FILESEXTRAPATHS_prepend := "${THISDIR}/files/" + +SRC_URI += "\ + file://asound.state \ +" + +do_install_append() { + install -v -d ${D}/var/lib/alsa/ + install -m 644 ${WORKDIR}/asound.state ${D}/var/lib/alsa/ +} + diff --git a/device-software/meta-edison-distro/recipes-multimedia/alsa/files/asound.state b/device-software/meta-edison-distro/recipes-multimedia/alsa/files/asound.state new file mode 100644 index 0000000..18aa6e2 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-multimedia/alsa/files/asound.state @@ -0,0 +1,14141 @@ +state.wm8958audio { + control.1 { + iface MIXER + name 'ssp1_out mux 0' + value fm + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 fm + item.1 bt + } + } + control.2 { + iface MIXER + name 'aware_out aware 0 switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.3 { + iface MIXER + name 'modem_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.4 { + iface MIXER + name 'modem_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.5 { + iface MIXER + name 'modem_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.6 { + iface MIXER + name 'modem_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.7 { + iface MIXER + name 'modem_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.8 { + iface MIXER + name 'modem_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.9 { + iface MIXER + name 'modem_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.10 { + iface MIXER + name 'modem_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.11 { + iface MIXER + name 'modem_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.12 { + iface MIXER + name 'modem_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.13 { + iface MIXER + name 'modem_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.14 { + iface MIXER + name 'modem_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.15 { + iface MIXER + name 'modem_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.16 { + iface MIXER + name 'modem_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.17 { + iface MIXER + name 'modem_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.18 { + iface MIXER + name 'modem_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.19 { + iface MIXER + name 'fm_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.20 { + iface MIXER + name 'fm_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.21 { + iface MIXER + name 'fm_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.22 { + iface MIXER + name 'fm_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.23 { + iface MIXER + name 'fm_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.24 { + iface MIXER + name 'fm_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.25 { + iface MIXER + name 'fm_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.26 { + iface MIXER + name 'fm_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.27 { + iface MIXER + name 'fm_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.28 { + iface MIXER + name 'fm_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.29 { + iface MIXER + name 'fm_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.30 { + iface MIXER + name 'fm_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.31 { + iface MIXER + name 'fm_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.32 { + iface MIXER + name 'fm_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.33 { + iface MIXER + name 'fm_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.34 { + iface MIXER + name 'fm_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.35 { + iface MIXER + name 'bt_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.36 { + iface MIXER + name 'bt_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.37 { + iface MIXER + name 'bt_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.38 { + iface MIXER + name 'bt_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.39 { + iface MIXER + name 'bt_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.40 { + iface MIXER + name 'bt_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.41 { + iface MIXER + name 'bt_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.42 { + iface MIXER + name 'bt_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.43 { + iface MIXER + name 'bt_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.44 { + iface MIXER + name 'bt_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.45 { + iface MIXER + name 'bt_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.46 { + iface MIXER + name 'bt_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.47 { + iface MIXER + name 'bt_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.48 { + iface MIXER + name 'bt_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.49 { + iface MIXER + name 'bt_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.50 { + iface MIXER + name 'bt_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.51 { + iface MIXER + name 'codec_out1 mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.52 { + iface MIXER + name 'codec_out1 mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.53 { + iface MIXER + name 'codec_out1 mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.54 { + iface MIXER + name 'codec_out1 mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.55 { + iface MIXER + name 'codec_out1 mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.56 { + iface MIXER + name 'codec_out1 mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.57 { + iface MIXER + name 'codec_out1 mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.58 { + iface MIXER + name 'codec_out1 mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.59 { + iface MIXER + name 'codec_out1 mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.60 { + iface MIXER + name 'codec_out1 mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.61 { + iface MIXER + name 'codec_out1 mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.62 { + iface MIXER + name 'codec_out1 mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.63 { + iface MIXER + name 'codec_out1 mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.64 { + iface MIXER + name 'codec_out1 mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.65 { + iface MIXER + name 'codec_out1 mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.66 { + iface MIXER + name 'codec_out1 mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.67 { + iface MIXER + name 'codec_out0 mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.68 { + iface MIXER + name 'codec_out0 mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.69 { + iface MIXER + name 'codec_out0 mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.70 { + iface MIXER + name 'codec_out0 mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.71 { + iface MIXER + name 'codec_out0 mix 0 sprot_loop_in' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.72 { + iface MIXER + name 'codec_out0 mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.73 { + iface MIXER + name 'codec_out0 mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.74 { + iface MIXER + name 'codec_out0 mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.75 { + iface MIXER + name 'codec_out0 mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.76 { + iface MIXER + name 'codec_out0 mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.77 { + iface MIXER + name 'codec_out0 mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.78 { + iface MIXER + name 'codec_out0 mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.79 { + iface MIXER + name 'codec_out0 mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.80 { + iface MIXER + name 'codec_out0 mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.81 { + iface MIXER + name 'codec_out0 mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.82 { + iface MIXER + name 'codec_out0 mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.83 { + iface MIXER + name 'rxspeech_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.84 { + iface MIXER + name 'rxspeech_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.85 { + iface MIXER + name 'rxspeech_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.86 { + iface MIXER + name 'rxspeech_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.87 { + iface MIXER + name 'rxspeech_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.88 { + iface MIXER + name 'rxspeech_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.89 { + iface MIXER + name 'rxspeech_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.90 { + iface MIXER + name 'rxspeech_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.91 { + iface MIXER + name 'rxspeech_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.92 { + iface MIXER + name 'rxspeech_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.93 { + iface MIXER + name 'rxspeech_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.94 { + iface MIXER + name 'rxspeech_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.95 { + iface MIXER + name 'rxspeech_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.96 { + iface MIXER + name 'rxspeech_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.97 { + iface MIXER + name 'rxspeech_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.98 { + iface MIXER + name 'rxspeech_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.99 { + iface MIXER + name 'speech_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.100 { + iface MIXER + name 'speech_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.101 { + iface MIXER + name 'speech_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.102 { + iface MIXER + name 'speech_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.103 { + iface MIXER + name 'speech_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.104 { + iface MIXER + name 'speech_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.105 { + iface MIXER + name 'speech_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.106 { + iface MIXER + name 'speech_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.107 { + iface MIXER + name 'speech_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.108 { + iface MIXER + name 'speech_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.109 { + iface MIXER + name 'speech_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.110 { + iface MIXER + name 'speech_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.111 { + iface MIXER + name 'speech_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.112 { + iface MIXER + name 'speech_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.113 { + iface MIXER + name 'speech_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.114 { + iface MIXER + name 'speech_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.115 { + iface MIXER + name 'hf_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.116 { + iface MIXER + name 'hf_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.117 { + iface MIXER + name 'hf_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.118 { + iface MIXER + name 'hf_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.119 { + iface MIXER + name 'hf_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.120 { + iface MIXER + name 'hf_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.121 { + iface MIXER + name 'hf_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.122 { + iface MIXER + name 'hf_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.123 { + iface MIXER + name 'hf_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.124 { + iface MIXER + name 'hf_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.125 { + iface MIXER + name 'hf_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.126 { + iface MIXER + name 'hf_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.127 { + iface MIXER + name 'hf_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.128 { + iface MIXER + name 'hf_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.129 { + iface MIXER + name 'hf_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.130 { + iface MIXER + name 'hf_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.131 { + iface MIXER + name 'hf_sns_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.132 { + iface MIXER + name 'hf_sns_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.133 { + iface MIXER + name 'hf_sns_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.134 { + iface MIXER + name 'hf_sns_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.135 { + iface MIXER + name 'hf_sns_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.136 { + iface MIXER + name 'hf_sns_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.137 { + iface MIXER + name 'hf_sns_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.138 { + iface MIXER + name 'hf_sns_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.139 { + iface MIXER + name 'hf_sns_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.140 { + iface MIXER + name 'hf_sns_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.141 { + iface MIXER + name 'hf_sns_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.142 { + iface MIXER + name 'hf_sns_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.143 { + iface MIXER + name 'hf_sns_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.144 { + iface MIXER + name 'hf_sns_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.145 { + iface MIXER + name 'hf_sns_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.146 { + iface MIXER + name 'hf_sns_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.147 { + iface MIXER + name 'vad_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.148 { + iface MIXER + name 'vad_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.149 { + iface MIXER + name 'vad_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.150 { + iface MIXER + name 'vad_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.151 { + iface MIXER + name 'vad_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.152 { + iface MIXER + name 'vad_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.153 { + iface MIXER + name 'vad_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.154 { + iface MIXER + name 'vad_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.155 { + iface MIXER + name 'vad_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.156 { + iface MIXER + name 'vad_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.157 { + iface MIXER + name 'vad_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.158 { + iface MIXER + name 'vad_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.159 { + iface MIXER + name 'vad_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.160 { + iface MIXER + name 'vad_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.161 { + iface MIXER + name 'vad_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.162 { + iface MIXER + name 'vad_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.163 { + iface MIXER + name 'aware_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.164 { + iface MIXER + name 'aware_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.165 { + iface MIXER + name 'aware_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.166 { + iface MIXER + name 'aware_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.167 { + iface MIXER + name 'aware_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.168 { + iface MIXER + name 'aware_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.169 { + iface MIXER + name 'aware_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.170 { + iface MIXER + name 'aware_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.171 { + iface MIXER + name 'aware_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.172 { + iface MIXER + name 'aware_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.173 { + iface MIXER + name 'aware_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.174 { + iface MIXER + name 'aware_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.175 { + iface MIXER + name 'aware_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.176 { + iface MIXER + name 'aware_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.177 { + iface MIXER + name 'aware_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.178 { + iface MIXER + name 'aware_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.179 { + iface MIXER + name 'voip_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.180 { + iface MIXER + name 'voip_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.181 { + iface MIXER + name 'voip_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.182 { + iface MIXER + name 'voip_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.183 { + iface MIXER + name 'voip_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.184 { + iface MIXER + name 'voip_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.185 { + iface MIXER + name 'voip_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.186 { + iface MIXER + name 'voip_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.187 { + iface MIXER + name 'voip_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.188 { + iface MIXER + name 'voip_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.189 { + iface MIXER + name 'voip_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.190 { + iface MIXER + name 'voip_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.191 { + iface MIXER + name 'voip_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.192 { + iface MIXER + name 'voip_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.193 { + iface MIXER + name 'voip_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.194 { + iface MIXER + name 'voip_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.195 { + iface MIXER + name 'media_loop2_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.196 { + iface MIXER + name 'media_loop2_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.197 { + iface MIXER + name 'media_loop2_out mix 0 codec_in0' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.198 { + iface MIXER + name 'media_loop2_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.199 { + iface MIXER + name 'media_loop2_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.200 { + iface MIXER + name 'media_loop2_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.201 { + iface MIXER + name 'media_loop2_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.202 { + iface MIXER + name 'media_loop2_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.203 { + iface MIXER + name 'media_loop2_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.204 { + iface MIXER + name 'media_loop2_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.205 { + iface MIXER + name 'media_loop2_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.206 { + iface MIXER + name 'media_loop2_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.207 { + iface MIXER + name 'media_loop2_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.208 { + iface MIXER + name 'media_loop2_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.209 { + iface MIXER + name 'media_loop2_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.210 { + iface MIXER + name 'media_loop2_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.211 { + iface MIXER + name 'media_loop1_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.212 { + iface MIXER + name 'media_loop1_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.213 { + iface MIXER + name 'media_loop1_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.214 { + iface MIXER + name 'media_loop1_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.215 { + iface MIXER + name 'media_loop1_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.216 { + iface MIXER + name 'media_loop1_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.217 { + iface MIXER + name 'media_loop1_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.218 { + iface MIXER + name 'media_loop1_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.219 { + iface MIXER + name 'media_loop1_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.220 { + iface MIXER + name 'media_loop1_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.221 { + iface MIXER + name 'media_loop1_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.222 { + iface MIXER + name 'media_loop1_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.223 { + iface MIXER + name 'media_loop1_out mix 0 pcm0_in' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.224 { + iface MIXER + name 'media_loop1_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.225 { + iface MIXER + name 'media_loop1_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.226 { + iface MIXER + name 'media_loop1_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.227 { + iface MIXER + name 'sprot_loop_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.228 { + iface MIXER + name 'sprot_loop_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.229 { + iface MIXER + name 'sprot_loop_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.230 { + iface MIXER + name 'sprot_loop_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.231 { + iface MIXER + name 'sprot_loop_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.232 { + iface MIXER + name 'sprot_loop_out mix 0 media_loop1_in' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.233 { + iface MIXER + name 'sprot_loop_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.234 { + iface MIXER + name 'sprot_loop_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.235 { + iface MIXER + name 'sprot_loop_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.236 { + iface MIXER + name 'sprot_loop_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.237 { + iface MIXER + name 'sprot_loop_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.238 { + iface MIXER + name 'sprot_loop_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.239 { + iface MIXER + name 'sprot_loop_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.240 { + iface MIXER + name 'sprot_loop_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.241 { + iface MIXER + name 'sprot_loop_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.242 { + iface MIXER + name 'sprot_loop_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.243 { + iface MIXER + name 'pcm2_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.244 { + iface MIXER + name 'pcm2_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.245 { + iface MIXER + name 'pcm2_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.246 { + iface MIXER + name 'pcm2_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.247 { + iface MIXER + name 'pcm2_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.248 { + iface MIXER + name 'pcm2_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.249 { + iface MIXER + name 'pcm2_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.250 { + iface MIXER + name 'pcm2_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.251 { + iface MIXER + name 'pcm2_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.252 { + iface MIXER + name 'pcm2_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.253 { + iface MIXER + name 'pcm2_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.254 { + iface MIXER + name 'pcm2_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.255 { + iface MIXER + name 'pcm2_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.256 { + iface MIXER + name 'pcm2_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.257 { + iface MIXER + name 'pcm2_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.258 { + iface MIXER + name 'pcm2_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.259 { + iface MIXER + name 'pcm1_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.260 { + iface MIXER + name 'pcm1_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.261 { + iface MIXER + name 'pcm1_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.262 { + iface MIXER + name 'pcm1_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.263 { + iface MIXER + name 'pcm1_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.264 { + iface MIXER + name 'pcm1_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.265 { + iface MIXER + name 'pcm1_out mix 0 media_loop2_in' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.266 { + iface MIXER + name 'pcm1_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.267 { + iface MIXER + name 'pcm1_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.268 { + iface MIXER + name 'pcm1_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.269 { + iface MIXER + name 'pcm1_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.270 { + iface MIXER + name 'pcm1_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.271 { + iface MIXER + name 'pcm1_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.272 { + iface MIXER + name 'pcm1_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.273 { + iface MIXER + name 'pcm1_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.274 { + iface MIXER + name 'pcm1_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.275 { + iface MIXER + name 'pcm0_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.276 { + iface MIXER + name 'pcm0_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.277 { + iface MIXER + name 'pcm0_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.278 { + iface MIXER + name 'pcm0_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.279 { + iface MIXER + name 'pcm0_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.280 { + iface MIXER + name 'pcm0_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.281 { + iface MIXER + name 'pcm0_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.282 { + iface MIXER + name 'pcm0_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.283 { + iface MIXER + name 'pcm0_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.284 { + iface MIXER + name 'pcm0_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.285 { + iface MIXER + name 'pcm0_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.286 { + iface MIXER + name 'pcm0_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.287 { + iface MIXER + name 'pcm0_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.288 { + iface MIXER + name 'pcm0_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.289 { + iface MIXER + name 'pcm0_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.290 { + iface MIXER + name 'pcm0_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.291 { + iface MIXER + name 'media1_out mix 0 media0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.292 { + iface MIXER + name 'media1_out mix 0 media1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.293 { + iface MIXER + name 'media1_out mix 0 media2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.294 { + iface MIXER + name 'media1_out mix 0 media3_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.295 { + iface MIXER + name 'media0_out mix 0 media0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.296 { + iface MIXER + name 'media0_out mix 0 media1_in' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.297 { + iface MIXER + name 'media0_out mix 0 media2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.298 { + iface MIXER + name 'media0_out mix 0 media3_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.299 { + iface MIXER + name 'media0_in gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.300 { + iface MIXER + name 'media0_in gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.301 { + iface MIXER + name 'media0_in gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.302 { + iface MIXER + name 'media1_in gain 0 rampduration' + value 50 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.303 { + iface MIXER + name 'media1_in gain 0 mute' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.304 { + iface MIXER + name 'media1_in gain 0 volume' + value.0 0 + value.1 0 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 0 + dbvalue.1 0 + } + } + control.305 { + iface MIXER + name 'media2_in gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.306 { + iface MIXER + name 'media2_in gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.307 { + iface MIXER + name 'media2_in gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.308 { + iface MIXER + name 'media3_in gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.309 { + iface MIXER + name 'media3_in gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.310 { + iface MIXER + name 'media3_in gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.311 { + iface MIXER + name 'pcm0_in gain 0 rampduration' + value 50 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.312 { + iface MIXER + name 'pcm0_in gain 0 mute' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.313 { + iface MIXER + name 'pcm0_in gain 0 volume' + value.0 0 + value.1 0 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 0 + dbvalue.1 0 + } + } + control.314 { + iface MIXER + name 'pcm1_in gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.315 { + iface MIXER + name 'pcm1_in gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.316 { + iface MIXER + name 'pcm1_in gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.317 { + iface MIXER + name 'low_pcm0_in gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.318 { + iface MIXER + name 'low_pcm0_in gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.319 { + iface MIXER + name 'low_pcm0_in gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.320 { + iface MIXER + name 'pcm1_out gain 0 rampduration' + value 50 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.321 { + iface MIXER + name 'pcm1_out gain 0 mute' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.322 { + iface MIXER + name 'pcm1_out gain 0 volume' + value.0 0 + value.1 0 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 0 + dbvalue.1 0 + } + } + control.323 { + iface MIXER + name 'pcm2_out gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.324 { + iface MIXER + name 'pcm2_out gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.325 { + iface MIXER + name 'pcm2_out gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.326 { + iface MIXER + name 'voip_in gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.327 { + iface MIXER + name 'voip_in gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.328 { + iface MIXER + name 'voip_in gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.329 { + iface MIXER + name 'voip_out gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.330 { + iface MIXER + name 'voip_out gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.331 { + iface MIXER + name 'voip_out gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.332 { + iface MIXER + name 'tone_in gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.333 { + iface MIXER + name 'tone_in gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.334 { + iface MIXER + name 'tone_in gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.335 { + iface MIXER + name 'aware_out gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.336 { + iface MIXER + name 'aware_out gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.337 { + iface MIXER + name 'aware_out gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.338 { + iface MIXER + name 'vad_out gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.339 { + iface MIXER + name 'vad_out gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.340 { + iface MIXER + name 'vad_out gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.341 { + iface MIXER + name 'hf_sns_out gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.342 { + iface MIXER + name 'hf_sns_out gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.343 { + iface MIXER + name 'hf_sns_out gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.344 { + iface MIXER + name 'hf_out gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.345 { + iface MIXER + name 'hf_out gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.346 { + iface MIXER + name 'hf_out gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.347 { + iface MIXER + name 'speech_out gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.348 { + iface MIXER + name 'speech_out gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.349 { + iface MIXER + name 'speech_out gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.350 { + iface MIXER + name 'txspeech_in gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.351 { + iface MIXER + name 'txspeech_in gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.352 { + iface MIXER + name 'txspeech_in gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.353 { + iface MIXER + name 'rxspeech_out gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.354 { + iface MIXER + name 'rxspeech_out gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.355 { + iface MIXER + name 'rxspeech_out gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.356 { + iface MIXER + name 'speech_in gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.357 { + iface MIXER + name 'speech_in gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.358 { + iface MIXER + name 'speech_in gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.359 { + iface MIXER + name 'codec_in0 gain 0 rampduration' + value 50 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.360 { + iface MIXER + name 'codec_in0 gain 0 mute' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.361 { + iface MIXER + name 'codec_in0 gain 0 volume' + value.0 0 + value.1 0 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 0 + dbvalue.1 0 + } + } + control.362 { + iface MIXER + name 'codec_in1 gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.363 { + iface MIXER + name 'codec_in1 gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.364 { + iface MIXER + name 'codec_in1 gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.365 { + iface MIXER + name 'codec_out0 gain 0 rampduration' + value 50 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.366 { + iface MIXER + name 'codec_out0 gain 0 mute' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.367 { + iface MIXER + name 'codec_out0 gain 0 volume' + value.0 0 + value.1 0 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 0 + dbvalue.1 0 + } + } + control.368 { + iface MIXER + name 'codec_out1 gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.369 { + iface MIXER + name 'codec_out1 gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.370 { + iface MIXER + name 'codec_out1 gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.371 { + iface MIXER + name 'bt_out gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.372 { + iface MIXER + name 'bt_out gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.373 { + iface MIXER + name 'bt_out gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.374 { + iface MIXER + name 'fm_out gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.375 { + iface MIXER + name 'fm_out gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.376 { + iface MIXER + name 'fm_out gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.377 { + iface MIXER + name 'bt_in gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.378 { + iface MIXER + name 'bt_in gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.379 { + iface MIXER + name 'bt_in gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.380 { + iface MIXER + name 'fm_in gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.381 { + iface MIXER + name 'fm_in gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.382 { + iface MIXER + name 'fm_in gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.383 { + iface MIXER + name 'modem_in gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.384 { + iface MIXER + name 'modem_in gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.385 { + iface MIXER + name 'modem_in gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.386 { + iface MIXER + name 'modem_out gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.387 { + iface MIXER + name 'modem_out gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.388 { + iface MIXER + name 'modem_out gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.389 { + iface MIXER + name 'media_loop1_out gain 0 rampduration' + value 50 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.390 { + iface MIXER + name 'media_loop1_out gain 0 mute' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.391 { + iface MIXER + name 'media_loop1_out gain 0 volume' + value.0 0 + value.1 0 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 0 + dbvalue.1 0 + } + } + control.392 { + iface MIXER + name 'media_loop2_out gain 0 rampduration' + value 50 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.393 { + iface MIXER + name 'media_loop2_out gain 0 mute' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.394 { + iface MIXER + name 'media_loop2_out gain 0 volume' + value.0 0 + value.1 0 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 0 + dbvalue.1 0 + } + } + control.395 { + iface MIXER + name 'sprot_loop_out gain 0 rampduration' + value 50 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.396 { + iface MIXER + name 'sprot_loop_out gain 0 mute' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.397 { + iface MIXER + name 'sprot_loop_out gain 0 volume' + value.0 0 + value.1 0 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 0 + dbvalue.1 0 + } + } + control.398 { + iface MIXER + name 'media0_in volume 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.399 { + iface MIXER + name 'media0_in volume 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.400 { + iface MIXER + name 'media0_in volume 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.401 { + iface MIXER + name 'sidetone_in gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.402 { + iface MIXER + name 'sidetone_in gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.403 { + iface MIXER + name 'sidetone_in gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.404 { + iface MIXER + name 'speech_out gain 1 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.405 { + iface MIXER + name 'speech_out gain 1 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.406 { + iface MIXER + name 'speech_out gain 1 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.407 { + iface MIXER + name 'media_loop1_out fir 0 params' + value '8a0003000100400000000000150014001400130011000e0009000000f3ffe0ffc8ffa8ff81ff52ff1bffddfe97fe4afef8fda1fd47fdecfc92fc3afce7fb9afb56fb1bfbecfac9fab4fa707ab4fac9faecfa1bfb56fb9afbe7fb3afc92fcecfc47fda1fdf8fd4afe97feddfe1bff52ff81ffa8ffc8ffe0fff3ff000009000e0000000000000000000000' + comment { + access 'read write' + type BYTES + count 138 + } + } + control.408 { + iface MIXER + name 'media_loop1_out iir 0 params' + value '98000300010001005a5aefcdd0ca5c8c2e74006f573a0022518b006f573a000100005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 300 + } + } + control.409 { + iface MIXER + name 'media_loop1_out mdrp 0 params' + value '4c00030000000100010000000000000000000000000000000000000000000000000000500000003c000000060000001900000001000058020000000100001400000014000000400000000001' + comment { + access 'read write' + type BYTES + count 76 + } + } + control.410 { + iface MIXER + name 'media_loop2_out fir 0 params' + value '100103000300400000000000fcffeaffdeffc6ffbbffa7ffa5ffa3ffb9ffc3ffdcffdcfff2ffeffffbffeaff150014001a00e5ff1800990083013900ddfda1fe51066209460c6e01a204de40a2046e01460c62095106a1feddfd3900830199001800e5ff1a0014001500eafffbffeffff2ffdcffdcffc3ffb9ffa3ffa5ffa7ff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 272 + } + } + control.411 { + iface MIXER + name 'media_loop2_out iir 0 params' + value '2c010300030001005a5aefcdd0ca5c8c2e74006f573a0022518b006f573a1f0000005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 300 + } + } + control.412 { + iface MIXER + name 'media_loop2_out mdrp 0 params' + value '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 76 + } + } + control.413 { + iface MIXER + name 'aware_out fir 0 params' + value '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 272 + } + } + control.414 { + iface MIXER + name 'aware_out iir 0 params' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 300 + } + } + control.415 { + iface MIXER + name 'aware_out aware 0 params' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 48 + } + } + control.416 { + iface MIXER + name 'vad_out fir 0 params' + value '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 272 + } + } + control.417 { + iface MIXER + name 'vad_out iir 0 params' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 300 + } + } + control.418 { + iface MIXER + name 'sprot_loop_out lpro 0 params' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 192 + } + } + control.419 { + iface MIXER + name 'codec_in0 dcr 0 params' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 300 + } + } + control.420 { + iface MIXER + name 'codec_in1 dcr 0 params' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 300 + } + } + control.421 { + iface MIXER + name 'speech_out ul_module 0 params fir_speech' + value '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 136 + } + } + control.422 { + iface MIXER + name 'speech_out ul_module 0 params fir_hf_sns' + value '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 136 + } + } + control.423 { + iface MIXER + name 'speech_out ul_module 0 params iir_speech' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 48 + } + } + control.424 { + iface MIXER + name 'speech_out ul_module 0 params iir_hf_sns' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 48 + } + } + control.425 { + iface MIXER + name 'speech_out ul_module 0 params aec' + value '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 640 + } + } + control.426 { + iface MIXER + name 'speech_out ul_module 0 params nr' + value '0000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 38 + } + } + control.427 { + iface MIXER + name 'speech_out ul_module 0 params agc' + value '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 58 + } + } + control.428 { + iface MIXER + name 'speech_out ul_module 0 params biquad' + value '00000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 22 + } + } + control.429 { + iface MIXER + name 'speech_out ul_module 0 params compr' + value '000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 36 + } + } + control.430 { + iface MIXER + name 'speech_out ul_module 0 params sns' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 324 + } + } + control.431 { + iface MIXER + name 'speech_out ul_module 0 params ser' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 42 + } + } + control.432 { + iface MIXER + name 'speech_out ul_module 0 params cni' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 48 + } + } + control.433 { + iface MIXER + name 'speech_out ul_module 0 params ref' + value '000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 24 + } + } + control.434 { + iface MIXER + name 'speech_out ul_module 0 params delay' + value '000000000000' + comment { + access 'read write' + type BYTES + count 6 + } + } + control.435 { + iface MIXER + name 'speech_out ul_module 0 params bmf' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 264 + } + } + control.436 { + iface MIXER + name 'speech_out ul_module 0 params dnr' + value '000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 18 + } + } + control.437 { + iface MIXER + name 'speech_in dl_module 0 params ana' + value '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 52 + } + } + control.438 { + iface MIXER + name 'speech_in dl_module 0 params fir' + value '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 136 + } + } + control.439 { + iface MIXER + name 'speech_in dl_module 0 params iir' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 48 + } + } + control.440 { + iface MIXER + name 'speech_in dl_module 0 params nr' + value '0000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 38 + } + } + control.441 { + iface MIXER + name 'speech_in dl_module 0 params biquad' + value '00000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 22 + } + } + control.442 { + iface MIXER + name 'speech_in dl_module 0 params compr' + value '000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 36 + } + } + control.443 { + iface MIXER + name 'speech_in dl_module 0 params cni' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 48 + } + } + control.444 { + iface MIXER + name 'speech_in dl_module 0 params bwx' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 54 + } + } + control.445 { + iface MIXER + name 'speech_in dl_module 0 params gmm' + value '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 586 + } + } + control.446 { + iface MIXER + name 'speech_in dl_module 0 params glc' + value '000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 18 + } + } + control.447 { + iface MIXER + name 'codec_out interleaver slot 0' + value codec_out0_0 + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 none + item.1 codec_out0_0 + item.2 codec_out0_1 + item.3 codec_out1_0 + item.4 codec_out1_1 + } + } + control.448 { + iface MIXER + name 'codec_out interleaver slot 1' + value codec_out0_1 + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 none + item.1 codec_out0_0 + item.2 codec_out0_1 + item.3 codec_out1_0 + item.4 codec_out1_1 + } + } + control.449 { + iface MIXER + name 'codec_out interleaver slot 2' + value codec_out1_0 + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 none + item.1 codec_out0_0 + item.2 codec_out0_1 + item.3 codec_out1_0 + item.4 codec_out1_1 + } + } + control.450 { + iface MIXER + name 'codec_out interleaver slot 3' + value codec_out1_1 + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 none + item.1 codec_out0_0 + item.2 codec_out0_1 + item.3 codec_out1_0 + item.4 codec_out1_1 + } + } + control.451 { + iface MIXER + name 'codec_in deinterleaver codec_in0_0' + value 'slot 0' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 none + item.1 'slot 0' + item.2 'slot 1' + item.3 'slot 2' + item.4 'slot 3' + } + } + control.452 { + iface MIXER + name 'codec_in deinterleaver codec_in0_1' + value 'slot 1' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 none + item.1 'slot 0' + item.2 'slot 1' + item.3 'slot 2' + item.4 'slot 3' + } + } + control.453 { + iface MIXER + name 'codec_in deinterleaver codec_in1_0' + value none + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 none + item.1 'slot 0' + item.2 'slot 1' + item.3 'slot 2' + item.4 'slot 3' + } + } + control.454 { + iface MIXER + name 'codec_in deinterleaver codec_in1_1' + value none + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 none + item.1 'slot 0' + item.2 'slot 1' + item.3 'slot 2' + item.4 'slot 3' + } + } + control.455 { + iface MIXER + name 'domain voice mode 0' + value narrowband + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 narrowband + item.1 wideband + } + } + control.456 { + iface MIXER + name 'domain bt mode 0' + value narrowband + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 narrowband + item.1 wideband + } + } + control.457 { + iface MIXER + name 'sst debug byte control' + value '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 512 + } + } + control.458 { + iface MIXER + name 'probe out0 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.459 { + iface MIXER + name 'probe out1 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.460 { + iface MIXER + name 'probe out2 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.461 { + iface MIXER + name 'probe out3 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.462 { + iface MIXER + name 'probe out4 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.463 { + iface MIXER + name 'probe out5 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.464 { + iface MIXER + name 'probe out6 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.465 { + iface MIXER + name 'probe out7 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.466 { + iface MIXER + name 'probe in0 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.467 { + iface MIXER + name 'probe in1 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.468 { + iface MIXER + name 'probe in2 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.469 { + iface MIXER + name 'probe in3 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.470 { + iface MIXER + name 'probe in4 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.471 { + iface MIXER + name 'probe in5 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.472 { + iface MIXER + name 'probe in6 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.473 { + iface MIXER + name 'probe in7 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.474 { + iface MIXER + name 'AIF1.1 DRC' + value '00980845000000000000' + comment { + access 'read write' + type BYTES + count 10 + } + } + control.475 { + iface MIXER + name 'AIF1.2 DRC' + value '00980845000000000000' + comment { + access 'read write' + type BYTES + count 10 + } + } + control.476 { + iface MIXER + name 'AIF2 DRC' + value '00980845000000000000' + comment { + access 'read write' + type BYTES + count 10 + } + } + control.477 { + iface MIXER + name 'AIF1DAC1 EQ1 Volume' + value 12 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 31' + dbmin -1200 + dbmax 1900 + dbvalue.0 0 + } + } + control.478 { + iface MIXER + name 'AIF1DAC1 EQ2 Volume' + value 12 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 31' + dbmin -1200 + dbmax 1900 + dbvalue.0 0 + } + } + control.479 { + iface MIXER + name 'AIF1DAC1 EQ3 Volume' + value 12 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 31' + dbmin -1200 + dbmax 1900 + dbvalue.0 0 + } + } + control.480 { + iface MIXER + name 'AIF1DAC1 EQ4 Volume' + value 12 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 31' + dbmin -1200 + dbmax 1900 + dbvalue.0 0 + } + } + control.481 { + iface MIXER + name 'AIF1DAC1 EQ5 Volume' + value 12 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 31' + dbmin -1200 + dbmax 1900 + dbvalue.0 0 + } + } + control.482 { + iface MIXER + name 'AIF1DAC2 EQ1 Volume' + value 12 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 31' + dbmin -1200 + dbmax 1900 + dbvalue.0 0 + } + } + control.483 { + iface MIXER + name 'AIF1DAC2 EQ2 Volume' + value 12 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 31' + dbmin -1200 + dbmax 1900 + dbvalue.0 0 + } + } + control.484 { + iface MIXER + name 'AIF1DAC2 EQ3 Volume' + value 12 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 31' + dbmin -1200 + dbmax 1900 + dbvalue.0 0 + } + } + control.485 { + iface MIXER + name 'AIF1DAC2 EQ4 Volume' + value 12 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 31' + dbmin -1200 + dbmax 1900 + dbvalue.0 0 + } + } + control.486 { + iface MIXER + name 'AIF1DAC2 EQ5 Volume' + value 12 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 31' + dbmin -1200 + dbmax 1900 + dbvalue.0 0 + } + } + control.487 { + iface MIXER + name 'AIF2 EQ1 Volume' + value 12 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 31' + dbmin -1200 + dbmax 1900 + dbvalue.0 0 + } + } + control.488 { + iface MIXER + name 'AIF2 EQ2 Volume' + value 12 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 31' + dbmin -1200 + dbmax 1900 + dbvalue.0 0 + } + } + control.489 { + iface MIXER + name 'AIF2 EQ3 Volume' + value 12 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 31' + dbmin -1200 + dbmax 1900 + dbvalue.0 0 + } + } + control.490 { + iface MIXER + name 'AIF2 EQ4 Volume' + value 12 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 31' + dbmin -1200 + dbmax 1900 + dbvalue.0 0 + } + } + control.491 { + iface MIXER + name 'AIF2 EQ5 Volume' + value 12 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 31' + dbmin -1200 + dbmax 1900 + dbvalue.0 0 + } + } + control.492 { + iface MIXER + name 'IN1L Volume' + value 11 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 31' + dbmin -1650 + dbmax 3000 + dbvalue.0 0 + } + } + control.493 { + iface MIXER + name 'IN1L Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.494 { + iface MIXER + name 'IN1L ZC Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.495 { + iface MIXER + name 'IN1R Volume' + value 11 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 31' + dbmin -1650 + dbmax 3000 + dbvalue.0 0 + } + } + control.496 { + iface MIXER + name 'IN1R Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.497 { + iface MIXER + name 'IN1R ZC Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.498 { + iface MIXER + name 'IN2L Volume' + value 11 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 31' + dbmin -1650 + dbmax 3000 + dbvalue.0 0 + } + } + control.499 { + iface MIXER + name 'IN2L Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.500 { + iface MIXER + name 'IN2L ZC Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.501 { + iface MIXER + name 'IN2R Volume' + value 11 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 31' + dbmin -1650 + dbmax 3000 + dbvalue.0 0 + } + } + control.502 { + iface MIXER + name 'IN2R Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.503 { + iface MIXER + name 'IN2R ZC Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.504 { + iface MIXER + name 'MIXINL IN2L Volume' + value 0 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 1' + dbmin 0 + dbmax 3000 + dbvalue.0 0 + } + } + control.505 { + iface MIXER + name 'MIXINL IN1L Volume' + value 0 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 1' + dbmin 0 + dbmax 3000 + dbvalue.0 0 + } + } + control.506 { + iface MIXER + name 'MIXINL Output Record Volume' + value 0 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 7' + dbmin -9999999 + dbmax 600 + dbvalue.0 -9999999 + } + } + control.507 { + iface MIXER + name 'MIXINL IN1LP Volume' + value 0 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 7' + dbmin -9999999 + dbmax 600 + dbvalue.0 -9999999 + } + } + control.508 { + iface MIXER + name 'MIXINL Direct Voice Volume' + value 0 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 6' + dbmin -9999999 + dbmax 300 + dbvalue.0 -9999999 + } + } + control.509 { + iface MIXER + name 'MIXINR IN2R Volume' + value 0 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 1' + dbmin 0 + dbmax 3000 + dbvalue.0 0 + } + } + control.510 { + iface MIXER + name 'MIXINR IN1R Volume' + value 0 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 1' + dbmin 0 + dbmax 3000 + dbvalue.0 0 + } + } + control.511 { + iface MIXER + name 'MIXINR Output Record Volume' + value 0 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 7' + dbmin -9999999 + dbmax 600 + dbvalue.0 -9999999 + } + } + control.512 { + iface MIXER + name 'MIXINR IN1RP Volume' + value 0 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 7' + dbmin -9999999 + dbmax 600 + dbvalue.0 -9999999 + } + } + control.513 { + iface MIXER + name 'MIXINR Direct Voice Volume' + value 0 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 6' + dbmin -9999999 + dbmax 300 + dbvalue.0 -9999999 + } + } + control.514 { + iface MIXER + name 'Left Output Mixer IN2RN Volume' + value 7 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 7' + dbmin -2100 + dbmax 0 + dbvalue.0 0 + } + } + control.515 { + iface MIXER + name 'Left Output Mixer IN2LN Volume' + value 7 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 7' + dbmin -2100 + dbmax 0 + dbvalue.0 0 + } + } + control.516 { + iface MIXER + name 'Left Output Mixer IN2LP Volume' + value 7 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 7' + dbmin -2100 + dbmax 0 + dbvalue.0 0 + } + } + control.517 { + iface MIXER + name 'Left Output Mixer IN1L Volume' + value 7 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 7' + dbmin -2100 + dbmax 0 + dbvalue.0 0 + } + } + control.518 { + iface MIXER + name 'Left Output Mixer IN1R Volume' + value 7 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 7' + dbmin -2100 + dbmax 0 + dbvalue.0 0 + } + } + control.519 { + iface MIXER + name 'Left Output Mixer Right Input Volume' + value 7 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 7' + dbmin -2100 + dbmax 0 + dbvalue.0 0 + } + } + control.520 { + iface MIXER + name 'Left Output Mixer Left Input Volume' + value 7 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 7' + dbmin -2100 + dbmax 0 + dbvalue.0 0 + } + } + control.521 { + iface MIXER + name 'Left Output Mixer DAC Volume' + value 7 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 7' + dbmin -2100 + dbmax 0 + dbvalue.0 0 + } + } + control.522 { + iface MIXER + name 'Right Output Mixer IN2LN Volume' + value 7 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 7' + dbmin -2100 + dbmax 0 + dbvalue.0 0 + } + } + control.523 { + iface MIXER + name 'Right Output Mixer IN2RN Volume' + value 7 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 7' + dbmin -2100 + dbmax 0 + dbvalue.0 0 + } + } + control.524 { + iface MIXER + name 'Right Output Mixer IN1L Volume' + value 7 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 7' + dbmin -2100 + dbmax 0 + dbvalue.0 0 + } + } + control.525 { + iface MIXER + name 'Right Output Mixer IN1R Volume' + value 7 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 7' + dbmin -2100 + dbmax 0 + dbvalue.0 0 + } + } + control.526 { + iface MIXER + name 'Right Output Mixer IN2RP Volume' + value 7 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 7' + dbmin -2100 + dbmax 0 + dbvalue.0 0 + } + } + control.527 { + iface MIXER + name 'Right Output Mixer Left Input Volume' + value 7 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 7' + dbmin -2100 + dbmax 0 + dbvalue.0 0 + } + } + control.528 { + iface MIXER + name 'Right Output Mixer Right Input Volume' + value 7 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 7' + dbmin -2100 + dbmax 0 + dbvalue.0 0 + } + } + control.529 { + iface MIXER + name 'Right Output Mixer DAC Volume' + value 7 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 7' + dbmin -2100 + dbmax 0 + dbvalue.0 0 + } + } + control.530 { + iface MIXER + name 'Output Volume' + value.0 57 + value.1 57 + comment { + access 'read write' + type INTEGER + count 2 + range '0 - 63' + dbmin -5700 + dbmax 600 + dbvalue.0 0 + dbvalue.1 0 + } + } + control.531 { + iface MIXER + name 'Output Switch' + value.0 true + value.1 true + comment { + access 'read write' + type BOOLEAN + count 2 + } + } + control.532 { + iface MIXER + name 'Output ZC Switch' + value.0 true + value.1 true + comment { + access 'read write' + type BOOLEAN + count 2 + } + } + control.533 { + iface MIXER + name 'Earpiece Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.534 { + iface MIXER + name 'Earpiece Volume' + value 1 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 1' + dbmin -600 + dbmax 0 + dbvalue.0 0 + } + } + control.535 { + iface MIXER + name 'SPKL Input Volume' + value 1 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 1' + dbmin -300 + dbmax 0 + dbvalue.0 0 + } + } + control.536 { + iface MIXER + name 'SPKL IN1LP Volume' + value 1 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 1' + dbmin -300 + dbmax 0 + dbvalue.0 0 + } + } + control.537 { + iface MIXER + name 'SPKL Output Volume' + value 1 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 1' + dbmin -300 + dbmax 0 + dbvalue.0 0 + } + } + control.538 { + iface MIXER + name 'SPKR Input Volume' + value 1 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 1' + dbmin -300 + dbmax 0 + dbvalue.0 0 + } + } + control.539 { + iface MIXER + name 'SPKR IN1RP Volume' + value 1 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 1' + dbmin -300 + dbmax 0 + dbvalue.0 0 + } + } + control.540 { + iface MIXER + name 'SPKR Output Volume' + value 1 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 1' + dbmin -300 + dbmax 0 + dbvalue.0 0 + } + } + control.541 { + iface MIXER + name 'Speaker Mixer Volume' + value.0 3 + value.1 3 + comment { + access 'read write' + type INTEGER + count 2 + range '0 - 3' + dbmin -9999999 + dbmax 0 + dbvalue.0 0 + dbvalue.1 0 + } + } + control.542 { + iface MIXER + name 'Speaker Volume' + value.0 57 + value.1 57 + comment { + access 'read write' + type INTEGER + count 2 + range '0 - 63' + dbmin -5700 + dbmax 600 + dbvalue.0 0 + dbvalue.1 0 + } + } + control.543 { + iface MIXER + name 'Speaker Switch' + value.0 true + value.1 true + comment { + access 'read write' + type BOOLEAN + count 2 + } + } + control.544 { + iface MIXER + name 'Speaker ZC Switch' + value.0 true + value.1 true + comment { + access 'read write' + type BOOLEAN + count 2 + } + } + control.545 { + iface MIXER + name 'Speaker Boost Volume' + value.0 0 + value.1 0 + comment { + access 'read write' + type INTEGER + count 2 + range '0 - 7' + dbmin 0 + dbmax 1200 + dbvalue.0 0 + dbvalue.1 0 + } + } + control.546 { + iface MIXER + name 'Speaker Reference' + value SPKVDD/2 + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 SPKVDD/2 + item.1 VMID + } + } + control.547 { + iface MIXER + name 'Speaker Mode' + value 'Class D' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'Class D' + item.1 'Class AB' + } + } + control.548 { + iface MIXER + name 'Headphone Volume' + value.0 45 + value.1 45 + comment { + access 'read write' + type INTEGER + count 2 + range '0 - 63' + dbmin -5700 + dbmax 600 + dbvalue.0 -1200 + dbvalue.1 -1200 + } + } + control.549 { + iface MIXER + name 'Headphone Switch' + value.0 true + value.1 true + comment { + access 'read write' + type BOOLEAN + count 2 + } + } + control.550 { + iface MIXER + name 'Headphone ZC Switch' + value.0 true + value.1 true + comment { + access 'read write' + type BOOLEAN + count 2 + } + } + control.551 { + iface MIXER + name 'LINEOUT1N Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.552 { + iface MIXER + name 'LINEOUT1P Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.553 { + iface MIXER + name 'LINEOUT1 Volume' + value 1 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 1' + dbmin -600 + dbmax 0 + dbvalue.0 0 + } + } + control.554 { + iface MIXER + name 'LINEOUT2N Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.555 { + iface MIXER + name 'LINEOUT2P Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.556 { + iface MIXER + name 'LINEOUT2 Volume' + value 1 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 1' + dbmin -600 + dbmax 0 + dbvalue.0 0 + } + } + control.557 { + iface MIXER + name 'AIF1ADC1 Volume' + value.0 110 + value.1 110 + comment { + access 'read write' + type INTEGER + count 2 + range '0 - 120' + dbmin -9999999 + dbmax 1800 + dbvalue.0 1050 + dbvalue.1 1050 + } + } + control.558 { + iface MIXER + name 'AIF1ADC2 Volume' + value.0 96 + value.1 96 + comment { + access 'read write' + type INTEGER + count 2 + range '0 - 120' + dbmin -9999999 + dbmax 1800 + dbvalue.0 0 + dbvalue.1 0 + } + } + control.559 { + iface MIXER + name 'AIF2ADC Volume' + value.0 96 + value.1 96 + comment { + access 'read write' + type INTEGER + count 2 + range '0 - 120' + dbmin -9999999 + dbmax 1800 + dbvalue.0 0 + dbvalue.1 0 + } + } + control.560 { + iface MIXER + name 'AIF1ADCL Source' + value Left + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 Left + item.1 Right + } + } + control.561 { + iface MIXER + name 'AIF1ADCR Source' + value Right + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 Left + item.1 Right + } + } + control.562 { + iface MIXER + name 'AIF2ADCL Source' + value Left + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 Left + item.1 Right + } + } + control.563 { + iface MIXER + name 'AIF2ADCR Source' + value Right + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 Left + item.1 Right + } + } + control.564 { + iface MIXER + name 'AIF1DACL Source' + value Left + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 Left + item.1 Right + } + } + control.565 { + iface MIXER + name 'AIF1DACR Source' + value Right + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 Left + item.1 Right + } + } + control.566 { + iface MIXER + name 'AIF2DACL Source' + value Left + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 Left + item.1 Right + } + } + control.567 { + iface MIXER + name 'AIF2DACR Source' + value Right + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 Left + item.1 Right + } + } + control.568 { + iface MIXER + name 'AIF1DAC1 Volume' + value.0 96 + value.1 96 + comment { + access 'read write' + type INTEGER + count 2 + range '0 - 96' + dbmin -9999999 + dbmax 0 + dbvalue.0 0 + dbvalue.1 0 + } + } + control.569 { + iface MIXER + name 'AIF1DAC2 Volume' + value.0 96 + value.1 96 + comment { + access 'read write' + type INTEGER + count 2 + range '0 - 96' + dbmin -9999999 + dbmax 0 + dbvalue.0 0 + dbvalue.1 0 + } + } + control.570 { + iface MIXER + name 'AIF2DAC Volume' + value.0 96 + value.1 96 + comment { + access 'read write' + type INTEGER + count 2 + range '0 - 96' + dbmin -9999999 + dbmax 0 + dbvalue.0 0 + dbvalue.1 0 + } + } + control.571 { + iface MIXER + name 'AIF1 Boost Volume' + value 0 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 3' + dbmin 0 + dbmax 1800 + dbvalue.0 0 + } + } + control.572 { + iface MIXER + name 'AIF2 Boost Volume' + value 0 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 3' + dbmin 0 + dbmax 1800 + dbvalue.0 0 + } + } + control.573 { + iface MIXER + name 'AIF1DAC1 EQ Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.574 { + iface MIXER + name 'AIF1DAC2 EQ Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.575 { + iface MIXER + name 'AIF2 EQ Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.576 { + iface MIXER + name 'AIF1DAC1 DRC Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.577 { + iface MIXER + name 'AIF1ADC1L DRC Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.578 { + iface MIXER + name 'AIF1ADC1R DRC Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.579 { + iface MIXER + name 'AIF1DAC2 DRC Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.580 { + iface MIXER + name 'AIF1ADC2L DRC Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.581 { + iface MIXER + name 'AIF1ADC2R DRC Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.582 { + iface MIXER + name 'AIF2DAC DRC Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.583 { + iface MIXER + name 'AIF2ADCL DRC Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.584 { + iface MIXER + name 'AIF2ADCR DRC Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.585 { + iface MIXER + name 'DAC1 Right Sidetone Volume' + value 0 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 12' + dbmin -3600 + dbmax 0 + dbvalue.0 -3600 + } + } + control.586 { + iface MIXER + name 'DAC1 Left Sidetone Volume' + value 0 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 12' + dbmin -3600 + dbmax 0 + dbvalue.0 -3600 + } + } + control.587 { + iface MIXER + name 'DAC2 Right Sidetone Volume' + value 0 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 12' + dbmin -3600 + dbmax 0 + dbvalue.0 -3600 + } + } + control.588 { + iface MIXER + name 'DAC2 Left Sidetone Volume' + value 0 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 12' + dbmin -3600 + dbmax 0 + dbvalue.0 -3600 + } + } + control.589 { + iface MIXER + name 'Sidetone HPF Mux' + value '2.7kHz' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '2.7kHz' + item.1 '1.35kHz' + item.2 '675Hz' + item.3 '370Hz' + item.4 '180Hz' + item.5 '90Hz' + item.6 '45Hz' + } + } + control.590 { + iface MIXER + name 'Sidetone HPF Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.591 { + iface MIXER + name 'AIF1ADC1 HPF Mode' + value HiFi + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 HiFi + item.1 'Voice 1' + item.2 'Voice 2' + item.3 'Voice 3' + } + } + control.592 { + iface MIXER + name 'AIF1ADC1 HPF Switch' + value.0 false + value.1 false + comment { + access 'read write' + type BOOLEAN + count 2 + } + } + control.593 { + iface MIXER + name 'AIF1ADC2 HPF Mode' + value HiFi + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 HiFi + item.1 'Voice 1' + item.2 'Voice 2' + item.3 'Voice 3' + } + } + control.594 { + iface MIXER + name 'AIF1ADC2 HPF Switch' + value.0 false + value.1 false + comment { + access 'read write' + type BOOLEAN + count 2 + } + } + control.595 { + iface MIXER + name 'AIF2ADC HPF Mode' + value HiFi + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 HiFi + item.1 'Voice 1' + item.2 'Voice 2' + item.3 'Voice 3' + } + } + control.596 { + iface MIXER + name 'AIF2ADC HPF Switch' + value.0 false + value.1 false + comment { + access 'read write' + type BOOLEAN + count 2 + } + } + control.597 { + iface MIXER + name 'ADC OSR' + value 'High Performance' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'Low Power' + item.1 'High Performance' + } + } + control.598 { + iface MIXER + name 'DAC OSR' + value 'Low Power' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'Low Power' + item.1 'High Performance' + } + } + control.599 { + iface MIXER + name 'DAC1 Volume' + value.0 96 + value.1 96 + comment { + access 'read write' + type INTEGER + count 2 + range '0 - 112' + dbmin -9999999 + dbmax 1200 + dbvalue.0 0 + dbvalue.1 0 + } + } + control.600 { + iface MIXER + name 'DAC1 Switch' + value.0 true + value.1 true + comment { + access 'read write' + type BOOLEAN + count 2 + } + } + control.601 { + iface MIXER + name 'DAC2 Volume' + value.0 96 + value.1 96 + comment { + access 'read write' + type INTEGER + count 2 + range '0 - 112' + dbmin -9999999 + dbmax 1200 + dbvalue.0 0 + dbvalue.1 0 + } + } + control.602 { + iface MIXER + name 'DAC2 Switch' + value.0 false + value.1 false + comment { + access 'read write' + type BOOLEAN + count 2 + } + } + control.603 { + iface MIXER + name 'SPKL DAC2 Volume' + value 1 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 1' + dbmin -300 + dbmax 0 + dbvalue.0 0 + } + } + control.604 { + iface MIXER + name 'SPKL DAC1 Volume' + value 1 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 1' + dbmin -300 + dbmax 0 + dbvalue.0 0 + } + } + control.605 { + iface MIXER + name 'SPKR DAC2 Volume' + value 1 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 1' + dbmin -300 + dbmax 0 + dbvalue.0 0 + } + } + control.606 { + iface MIXER + name 'SPKR DAC1 Volume' + value 1 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 1' + dbmin -300 + dbmax 0 + dbvalue.0 0 + } + } + control.607 { + iface MIXER + name 'AIF1DAC1 3D Stereo Volume' + value 0 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 15' + dbmin -1600 + dbmax 1145 + dbvalue.0 -1600 + } + } + control.608 { + iface MIXER + name 'AIF1DAC1 3D Stereo Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.609 { + iface MIXER + name 'AIF1DAC2 3D Stereo Volume' + value 0 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 15' + dbmin -1600 + dbmax 1145 + dbvalue.0 -1600 + } + } + control.610 { + iface MIXER + name 'AIF1DAC2 3D Stereo Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.611 { + iface MIXER + name 'AIF2DAC 3D Stereo Volume' + value 0 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 15' + dbmin -1600 + dbmax 1145 + dbvalue.0 -1600 + } + } + control.612 { + iface MIXER + name 'AIF2DAC 3D Stereo Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.613 { + iface MIXER + name 'MIXINL MIXOUTL Volume' + value 0 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 7' + dbmin 0 + dbmax 6300 + dbvalue.0 0 + } + } + control.614 { + iface MIXER + name 'MIXINR MIXOUTR Volume' + value 0 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 7' + dbmin 0 + dbmax 6300 + dbvalue.0 0 + } + } + control.615 { + iface MIXER + name 'AIF3 Boost Volume' + value 0 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 3' + dbmin 0 + dbmax 1800 + dbvalue.0 0 + } + } + control.616 { + iface MIXER + name 'AIF1DAC1 Noise Gate Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.617 { + iface MIXER + name 'AIF1DAC1 Noise Gate Hold Time' + value '30ms' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '30ms' + item.1 '125ms' + item.2 '250ms' + item.3 '500ms' + } + } + control.618 { + iface MIXER + name 'AIF1DAC1 Noise Gate Threshold Volume' + value 3 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 7' + dbmin -10200 + dbmax -6000 + dbvalue.0 -8400 + } + } + control.619 { + iface MIXER + name 'AIF1DAC2 Noise Gate Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.620 { + iface MIXER + name 'AIF1DAC2 Noise Gate Hold Time' + value '30ms' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '30ms' + item.1 '125ms' + item.2 '250ms' + item.3 '500ms' + } + } + control.621 { + iface MIXER + name 'AIF1DAC2 Noise Gate Threshold Volume' + value 3 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 7' + dbmin -10200 + dbmax -6000 + dbvalue.0 -8400 + } + } + control.622 { + iface MIXER + name 'AIF2DAC Noise Gate Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.623 { + iface MIXER + name 'AIF2DAC Noise Gate Hold Time' + value '30ms' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '30ms' + item.1 '125ms' + item.2 '250ms' + item.3 '500ms' + } + } + control.624 { + iface MIXER + name 'AIF2DAC Noise Gate Threshold Volume' + value 3 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 7' + dbmin -10200 + dbmax -6000 + dbvalue.0 -8400 + } + } + control.625 { + iface MIXER + name 'AIF1DAC1 MBC Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.626 { + iface MIXER + name 'AIF1DAC2 MBC Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.627 { + iface MIXER + name 'AIF2DAC MBC Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.628 { + iface MIXER + name 'AIF1DAC1 VSS Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.629 { + iface MIXER + name 'AIF1DAC2 VSS Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.630 { + iface MIXER + name 'AIF2DAC VSS Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.631 { + iface MIXER + name 'AIF1DAC1 HPF1 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.632 { + iface MIXER + name 'AIF1DAC2 HPF1 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.633 { + iface MIXER + name 'AIF2DAC HPF1 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.634 { + iface MIXER + name 'AIF1DAC1 HPF2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.635 { + iface MIXER + name 'AIF1DAC2 HPF2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.636 { + iface MIXER + name 'AIF2DAC HPF2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.637 { + iface MIXER + name 'AIF1DAC1 Enhanced EQ Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.638 { + iface MIXER + name 'AIF1DAC2 Enhanced EQ Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.639 { + iface MIXER + name 'AIF2DAC Enhanced EQ Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.640 { + iface MIXER + name 'ADCR Mux' + value DMIC + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 ADC + item.1 DMIC + } + } + control.641 { + iface MIXER + name 'ADCL Mux' + value DMIC + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 ADC + item.1 DMIC + } + } + control.642 { + iface MIXER + name 'Right Headphone Mux' + value Mixer + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 Mixer + item.1 DAC + } + } + control.643 { + iface MIXER + name 'Left Headphone Mux' + value Mixer + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 Mixer + item.1 DAC + } + } + control.644 { + iface MIXER + name 'SPKR DAC2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.645 { + iface MIXER + name 'SPKR Input Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.646 { + iface MIXER + name 'SPKR IN1RP Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.647 { + iface MIXER + name 'SPKR Output Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.648 { + iface MIXER + name 'SPKR DAC1 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.649 { + iface MIXER + name 'SPKL DAC2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.650 { + iface MIXER + name 'SPKL Input Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.651 { + iface MIXER + name 'SPKL IN1LP Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.652 { + iface MIXER + name 'SPKL Output Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.653 { + iface MIXER + name 'SPKL DAC1 Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.654 { + iface MIXER + name 'AIF3ADC Mux' + value AIF1ADCDAT + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 AIF1ADCDAT + item.1 AIF2ADCDAT + item.2 AIF2DACDAT + item.3 'Mono PCM' + } + } + control.655 { + iface MIXER + name 'AIF2DACR Mux' + value AIF2 + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 AIF2 + item.1 AIF3 + } + } + control.656 { + iface MIXER + name 'AIF2DACL Mux' + value AIF2 + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 AIF2 + item.1 AIF3 + } + } + control.657 { + iface MIXER + name 'Mono PCM Out Mux' + value None + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 None + item.1 AIF2ADCL + item.2 AIF2ADCR + } + } + control.658 { + iface MIXER + name 'AIF2 Loopback' + value None + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 None + item.1 ADCDAT + } + } + control.659 { + iface MIXER + name 'AIF1 Loopback' + value None + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 None + item.1 ADCDAT + } + } + control.660 { + iface MIXER + name 'AIF2ADC Mux' + value AIF2ADCDAT + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 AIF2ADCDAT + item.1 AIF3DACDAT + } + } + control.661 { + iface MIXER + name 'AIF2DAC Mux' + value AIF2DACDAT + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 AIF2DACDAT + item.1 AIF3DACDAT + } + } + control.662 { + iface MIXER + name 'AIF1DAC Mux' + value AIF1DACDAT + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 AIF1DACDAT + item.1 AIF3DACDAT + } + } + control.663 { + iface MIXER + name 'DAC1R Mixer Right Sidetone Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.664 { + iface MIXER + name 'DAC1R Mixer Left Sidetone Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.665 { + iface MIXER + name 'DAC1R Mixer AIF2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.666 { + iface MIXER + name 'DAC1R Mixer AIF1.2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.667 { + iface MIXER + name 'DAC1R Mixer AIF1.1 Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.668 { + iface MIXER + name 'DAC1L Mixer Right Sidetone Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.669 { + iface MIXER + name 'DAC1L Mixer Left Sidetone Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.670 { + iface MIXER + name 'DAC1L Mixer AIF2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.671 { + iface MIXER + name 'DAC1L Mixer AIF1.2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.672 { + iface MIXER + name 'DAC1L Mixer AIF1.1 Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.673 { + iface MIXER + name 'Right Sidetone' + value ADC/DMIC1 + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 ADC/DMIC1 + item.1 DMIC2 + } + } + control.674 { + iface MIXER + name 'Left Sidetone' + value ADC/DMIC1 + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 ADC/DMIC1 + item.1 DMIC2 + } + } + control.675 { + iface MIXER + name 'AIF2DAC2R Mixer Right Sidetone Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.676 { + iface MIXER + name 'AIF2DAC2R Mixer Left Sidetone Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.677 { + iface MIXER + name 'AIF2DAC2R Mixer AIF2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.678 { + iface MIXER + name 'AIF2DAC2R Mixer AIF1.2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.679 { + iface MIXER + name 'AIF2DAC2R Mixer AIF1.1 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.680 { + iface MIXER + name 'AIF2DAC2L Mixer Right Sidetone Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.681 { + iface MIXER + name 'AIF2DAC2L Mixer Left Sidetone Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.682 { + iface MIXER + name 'AIF2DAC2L Mixer AIF2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.683 { + iface MIXER + name 'AIF2DAC2L Mixer AIF1.2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.684 { + iface MIXER + name 'AIF2DAC2L Mixer AIF1.1 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.685 { + iface MIXER + name 'AIF1ADC2R Mixer DMIC Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.686 { + iface MIXER + name 'AIF1ADC2R Mixer AIF2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.687 { + iface MIXER + name 'AIF1ADC2L Mixer DMIC Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.688 { + iface MIXER + name 'AIF1ADC2L Mixer AIF2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.689 { + iface MIXER + name 'AIF1ADC1R Mixer ADC/DMIC Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.690 { + iface MIXER + name 'AIF1ADC1R Mixer AIF2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.691 { + iface MIXER + name 'AIF1ADC1L Mixer ADC/DMIC Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.692 { + iface MIXER + name 'AIF1ADC1L Mixer AIF2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.693 { + iface MIXER + name 'LINEOUT2P Mixer Right Output Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.694 { + iface MIXER + name 'LINEOUT2N Mixer Left Output Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.695 { + iface MIXER + name 'LINEOUT2N Mixer Right Output Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.696 { + iface MIXER + name 'LINEOUT1P Mixer Left Output Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.697 { + iface MIXER + name 'LINEOUT1N Mixer Left Output Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.698 { + iface MIXER + name 'LINEOUT1N Mixer Right Output Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.699 { + iface MIXER + name 'SPKR Boost Direct Voice Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.700 { + iface MIXER + name 'SPKR Boost SPKL Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.701 { + iface MIXER + name 'SPKR Boost SPKR Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.702 { + iface MIXER + name 'SPKL Boost Direct Voice Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.703 { + iface MIXER + name 'SPKL Boost SPKL Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.704 { + iface MIXER + name 'SPKL Boost SPKR Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.705 { + iface MIXER + name 'Earpiece Mixer Direct Voice Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.706 { + iface MIXER + name 'Earpiece Mixer Left Output Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.707 { + iface MIXER + name 'Earpiece Mixer Right Output Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.708 { + iface MIXER + name 'Right Output Mixer Left Input Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.709 { + iface MIXER + name 'Right Output Mixer Right Input Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.710 { + iface MIXER + name 'Right Output Mixer IN2LN Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.711 { + iface MIXER + name 'Right Output Mixer IN2RN Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.712 { + iface MIXER + name 'Right Output Mixer IN1L Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.713 { + iface MIXER + name 'Right Output Mixer IN1R Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.714 { + iface MIXER + name 'Right Output Mixer IN2RP Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.715 { + iface MIXER + name 'Right Output Mixer DAC Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.716 { + iface MIXER + name 'Left Output Mixer Right Input Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.717 { + iface MIXER + name 'Left Output Mixer Left Input Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.718 { + iface MIXER + name 'Left Output Mixer IN2RN Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.719 { + iface MIXER + name 'Left Output Mixer IN2LN Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.720 { + iface MIXER + name 'Left Output Mixer IN2LP Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.721 { + iface MIXER + name 'Left Output Mixer IN1R Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.722 { + iface MIXER + name 'Left Output Mixer IN1L Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.723 { + iface MIXER + name 'Left Output Mixer DAC Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.724 { + iface MIXER + name 'MIXINR IN2R Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.725 { + iface MIXER + name 'MIXINR IN1R Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.726 { + iface MIXER + name 'MIXINL IN2L Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.727 { + iface MIXER + name 'MIXINL IN1L Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.728 { + iface MIXER + name 'IN2R PGA IN2RP Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.729 { + iface MIXER + name 'IN2R PGA IN2RN Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.730 { + iface MIXER + name 'IN2L PGA IN2LP Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.731 { + iface MIXER + name 'IN2L PGA IN2LN Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.732 { + iface MIXER + name 'IN1R PGA IN1RP Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.733 { + iface MIXER + name 'IN1R PGA IN1RN Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.734 { + iface MIXER + name 'IN1L PGA IN1LP Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.735 { + iface MIXER + name 'IN1L PGA IN1LN Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } +} +state.dummyaudio { + control.1 { + iface MIXER + name 'ssp1_out mux 0' + value fm + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 fm + item.1 bt + } + } + control.2 { + iface MIXER + name 'aware_out aware 0 switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.3 { + iface MIXER + name 'modem_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.4 { + iface MIXER + name 'modem_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.5 { + iface MIXER + name 'modem_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.6 { + iface MIXER + name 'modem_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.7 { + iface MIXER + name 'modem_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.8 { + iface MIXER + name 'modem_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.9 { + iface MIXER + name 'modem_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.10 { + iface MIXER + name 'modem_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.11 { + iface MIXER + name 'modem_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.12 { + iface MIXER + name 'modem_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.13 { + iface MIXER + name 'modem_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.14 { + iface MIXER + name 'modem_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.15 { + iface MIXER + name 'modem_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.16 { + iface MIXER + name 'modem_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.17 { + iface MIXER + name 'modem_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.18 { + iface MIXER + name 'modem_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.19 { + iface MIXER + name 'fm_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.20 { + iface MIXER + name 'fm_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.21 { + iface MIXER + name 'fm_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.22 { + iface MIXER + name 'fm_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.23 { + iface MIXER + name 'fm_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.24 { + iface MIXER + name 'fm_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.25 { + iface MIXER + name 'fm_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.26 { + iface MIXER + name 'fm_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.27 { + iface MIXER + name 'fm_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.28 { + iface MIXER + name 'fm_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.29 { + iface MIXER + name 'fm_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.30 { + iface MIXER + name 'fm_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.31 { + iface MIXER + name 'fm_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.32 { + iface MIXER + name 'fm_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.33 { + iface MIXER + name 'fm_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.34 { + iface MIXER + name 'fm_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.35 { + iface MIXER + name 'bt_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.36 { + iface MIXER + name 'bt_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.37 { + iface MIXER + name 'bt_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.38 { + iface MIXER + name 'bt_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.39 { + iface MIXER + name 'bt_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.40 { + iface MIXER + name 'bt_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.41 { + iface MIXER + name 'bt_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.42 { + iface MIXER + name 'bt_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.43 { + iface MIXER + name 'bt_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.44 { + iface MIXER + name 'bt_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.45 { + iface MIXER + name 'bt_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.46 { + iface MIXER + name 'bt_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.47 { + iface MIXER + name 'bt_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.48 { + iface MIXER + name 'bt_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.49 { + iface MIXER + name 'bt_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.50 { + iface MIXER + name 'bt_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.51 { + iface MIXER + name 'codec_out1 mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.52 { + iface MIXER + name 'codec_out1 mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.53 { + iface MIXER + name 'codec_out1 mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.54 { + iface MIXER + name 'codec_out1 mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.55 { + iface MIXER + name 'codec_out1 mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.56 { + iface MIXER + name 'codec_out1 mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.57 { + iface MIXER + name 'codec_out1 mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.58 { + iface MIXER + name 'codec_out1 mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.59 { + iface MIXER + name 'codec_out1 mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.60 { + iface MIXER + name 'codec_out1 mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.61 { + iface MIXER + name 'codec_out1 mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.62 { + iface MIXER + name 'codec_out1 mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.63 { + iface MIXER + name 'codec_out1 mix 0 pcm0_in' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.64 { + iface MIXER + name 'codec_out1 mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.65 { + iface MIXER + name 'codec_out1 mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.66 { + iface MIXER + name 'codec_out1 mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.67 { + iface MIXER + name 'codec_out0 mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.68 { + iface MIXER + name 'codec_out0 mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.69 { + iface MIXER + name 'codec_out0 mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.70 { + iface MIXER + name 'codec_out0 mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.71 { + iface MIXER + name 'codec_out0 mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.72 { + iface MIXER + name 'codec_out0 mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.73 { + iface MIXER + name 'codec_out0 mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.74 { + iface MIXER + name 'codec_out0 mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.75 { + iface MIXER + name 'codec_out0 mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.76 { + iface MIXER + name 'codec_out0 mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.77 { + iface MIXER + name 'codec_out0 mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.78 { + iface MIXER + name 'codec_out0 mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.79 { + iface MIXER + name 'codec_out0 mix 0 pcm0_in' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.80 { + iface MIXER + name 'codec_out0 mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.81 { + iface MIXER + name 'codec_out0 mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.82 { + iface MIXER + name 'codec_out0 mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.83 { + iface MIXER + name 'rxspeech_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.84 { + iface MIXER + name 'rxspeech_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.85 { + iface MIXER + name 'rxspeech_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.86 { + iface MIXER + name 'rxspeech_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.87 { + iface MIXER + name 'rxspeech_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.88 { + iface MIXER + name 'rxspeech_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.89 { + iface MIXER + name 'rxspeech_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.90 { + iface MIXER + name 'rxspeech_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.91 { + iface MIXER + name 'rxspeech_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.92 { + iface MIXER + name 'rxspeech_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.93 { + iface MIXER + name 'rxspeech_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.94 { + iface MIXER + name 'rxspeech_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.95 { + iface MIXER + name 'rxspeech_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.96 { + iface MIXER + name 'rxspeech_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.97 { + iface MIXER + name 'rxspeech_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.98 { + iface MIXER + name 'rxspeech_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.99 { + iface MIXER + name 'speech_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.100 { + iface MIXER + name 'speech_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.101 { + iface MIXER + name 'speech_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.102 { + iface MIXER + name 'speech_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.103 { + iface MIXER + name 'speech_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.104 { + iface MIXER + name 'speech_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.105 { + iface MIXER + name 'speech_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.106 { + iface MIXER + name 'speech_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.107 { + iface MIXER + name 'speech_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.108 { + iface MIXER + name 'speech_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.109 { + iface MIXER + name 'speech_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.110 { + iface MIXER + name 'speech_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.111 { + iface MIXER + name 'speech_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.112 { + iface MIXER + name 'speech_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.113 { + iface MIXER + name 'speech_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.114 { + iface MIXER + name 'speech_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.115 { + iface MIXER + name 'hf_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.116 { + iface MIXER + name 'hf_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.117 { + iface MIXER + name 'hf_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.118 { + iface MIXER + name 'hf_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.119 { + iface MIXER + name 'hf_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.120 { + iface MIXER + name 'hf_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.121 { + iface MIXER + name 'hf_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.122 { + iface MIXER + name 'hf_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.123 { + iface MIXER + name 'hf_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.124 { + iface MIXER + name 'hf_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.125 { + iface MIXER + name 'hf_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.126 { + iface MIXER + name 'hf_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.127 { + iface MIXER + name 'hf_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.128 { + iface MIXER + name 'hf_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.129 { + iface MIXER + name 'hf_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.130 { + iface MIXER + name 'hf_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.131 { + iface MIXER + name 'hf_sns_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.132 { + iface MIXER + name 'hf_sns_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.133 { + iface MIXER + name 'hf_sns_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.134 { + iface MIXER + name 'hf_sns_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.135 { + iface MIXER + name 'hf_sns_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.136 { + iface MIXER + name 'hf_sns_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.137 { + iface MIXER + name 'hf_sns_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.138 { + iface MIXER + name 'hf_sns_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.139 { + iface MIXER + name 'hf_sns_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.140 { + iface MIXER + name 'hf_sns_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.141 { + iface MIXER + name 'hf_sns_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.142 { + iface MIXER + name 'hf_sns_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.143 { + iface MIXER + name 'hf_sns_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.144 { + iface MIXER + name 'hf_sns_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.145 { + iface MIXER + name 'hf_sns_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.146 { + iface MIXER + name 'hf_sns_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.147 { + iface MIXER + name 'vad_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.148 { + iface MIXER + name 'vad_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.149 { + iface MIXER + name 'vad_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.150 { + iface MIXER + name 'vad_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.151 { + iface MIXER + name 'vad_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.152 { + iface MIXER + name 'vad_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.153 { + iface MIXER + name 'vad_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.154 { + iface MIXER + name 'vad_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.155 { + iface MIXER + name 'vad_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.156 { + iface MIXER + name 'vad_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.157 { + iface MIXER + name 'vad_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.158 { + iface MIXER + name 'vad_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.159 { + iface MIXER + name 'vad_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.160 { + iface MIXER + name 'vad_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.161 { + iface MIXER + name 'vad_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.162 { + iface MIXER + name 'vad_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.163 { + iface MIXER + name 'aware_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.164 { + iface MIXER + name 'aware_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.165 { + iface MIXER + name 'aware_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.166 { + iface MIXER + name 'aware_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.167 { + iface MIXER + name 'aware_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.168 { + iface MIXER + name 'aware_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.169 { + iface MIXER + name 'aware_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.170 { + iface MIXER + name 'aware_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.171 { + iface MIXER + name 'aware_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.172 { + iface MIXER + name 'aware_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.173 { + iface MIXER + name 'aware_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.174 { + iface MIXER + name 'aware_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.175 { + iface MIXER + name 'aware_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.176 { + iface MIXER + name 'aware_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.177 { + iface MIXER + name 'aware_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.178 { + iface MIXER + name 'aware_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.179 { + iface MIXER + name 'voip_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.180 { + iface MIXER + name 'voip_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.181 { + iface MIXER + name 'voip_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.182 { + iface MIXER + name 'voip_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.183 { + iface MIXER + name 'voip_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.184 { + iface MIXER + name 'voip_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.185 { + iface MIXER + name 'voip_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.186 { + iface MIXER + name 'voip_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.187 { + iface MIXER + name 'voip_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.188 { + iface MIXER + name 'voip_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.189 { + iface MIXER + name 'voip_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.190 { + iface MIXER + name 'voip_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.191 { + iface MIXER + name 'voip_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.192 { + iface MIXER + name 'voip_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.193 { + iface MIXER + name 'voip_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.194 { + iface MIXER + name 'voip_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.195 { + iface MIXER + name 'media_loop2_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.196 { + iface MIXER + name 'media_loop2_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.197 { + iface MIXER + name 'media_loop2_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.198 { + iface MIXER + name 'media_loop2_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.199 { + iface MIXER + name 'media_loop2_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.200 { + iface MIXER + name 'media_loop2_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.201 { + iface MIXER + name 'media_loop2_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.202 { + iface MIXER + name 'media_loop2_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.203 { + iface MIXER + name 'media_loop2_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.204 { + iface MIXER + name 'media_loop2_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.205 { + iface MIXER + name 'media_loop2_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.206 { + iface MIXER + name 'media_loop2_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.207 { + iface MIXER + name 'media_loop2_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.208 { + iface MIXER + name 'media_loop2_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.209 { + iface MIXER + name 'media_loop2_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.210 { + iface MIXER + name 'media_loop2_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.211 { + iface MIXER + name 'media_loop1_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.212 { + iface MIXER + name 'media_loop1_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.213 { + iface MIXER + name 'media_loop1_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.214 { + iface MIXER + name 'media_loop1_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.215 { + iface MIXER + name 'media_loop1_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.216 { + iface MIXER + name 'media_loop1_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.217 { + iface MIXER + name 'media_loop1_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.218 { + iface MIXER + name 'media_loop1_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.219 { + iface MIXER + name 'media_loop1_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.220 { + iface MIXER + name 'media_loop1_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.221 { + iface MIXER + name 'media_loop1_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.222 { + iface MIXER + name 'media_loop1_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.223 { + iface MIXER + name 'media_loop1_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.224 { + iface MIXER + name 'media_loop1_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.225 { + iface MIXER + name 'media_loop1_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.226 { + iface MIXER + name 'media_loop1_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.227 { + iface MIXER + name 'sprot_loop_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.228 { + iface MIXER + name 'sprot_loop_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.229 { + iface MIXER + name 'sprot_loop_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.230 { + iface MIXER + name 'sprot_loop_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.231 { + iface MIXER + name 'sprot_loop_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.232 { + iface MIXER + name 'sprot_loop_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.233 { + iface MIXER + name 'sprot_loop_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.234 { + iface MIXER + name 'sprot_loop_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.235 { + iface MIXER + name 'sprot_loop_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.236 { + iface MIXER + name 'sprot_loop_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.237 { + iface MIXER + name 'sprot_loop_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.238 { + iface MIXER + name 'sprot_loop_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.239 { + iface MIXER + name 'sprot_loop_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.240 { + iface MIXER + name 'sprot_loop_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.241 { + iface MIXER + name 'sprot_loop_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.242 { + iface MIXER + name 'sprot_loop_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.243 { + iface MIXER + name 'pcm2_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.244 { + iface MIXER + name 'pcm2_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.245 { + iface MIXER + name 'pcm2_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.246 { + iface MIXER + name 'pcm2_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.247 { + iface MIXER + name 'pcm2_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.248 { + iface MIXER + name 'pcm2_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.249 { + iface MIXER + name 'pcm2_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.250 { + iface MIXER + name 'pcm2_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.251 { + iface MIXER + name 'pcm2_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.252 { + iface MIXER + name 'pcm2_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.253 { + iface MIXER + name 'pcm2_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.254 { + iface MIXER + name 'pcm2_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.255 { + iface MIXER + name 'pcm2_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.256 { + iface MIXER + name 'pcm2_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.257 { + iface MIXER + name 'pcm2_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.258 { + iface MIXER + name 'pcm2_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.259 { + iface MIXER + name 'pcm1_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.260 { + iface MIXER + name 'pcm1_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.261 { + iface MIXER + name 'pcm1_out mix 0 codec_in0' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.262 { + iface MIXER + name 'pcm1_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.263 { + iface MIXER + name 'pcm1_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.264 { + iface MIXER + name 'pcm1_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.265 { + iface MIXER + name 'pcm1_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.266 { + iface MIXER + name 'pcm1_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.267 { + iface MIXER + name 'pcm1_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.268 { + iface MIXER + name 'pcm1_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.269 { + iface MIXER + name 'pcm1_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.270 { + iface MIXER + name 'pcm1_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.271 { + iface MIXER + name 'pcm1_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.272 { + iface MIXER + name 'pcm1_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.273 { + iface MIXER + name 'pcm1_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.274 { + iface MIXER + name 'pcm1_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.275 { + iface MIXER + name 'pcm0_out mix 0 modem_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.276 { + iface MIXER + name 'pcm0_out mix 0 bt_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.277 { + iface MIXER + name 'pcm0_out mix 0 codec_in0' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.278 { + iface MIXER + name 'pcm0_out mix 0 codec_in1' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.279 { + iface MIXER + name 'pcm0_out mix 0 sprot_loop_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.280 { + iface MIXER + name 'pcm0_out mix 0 media_loop1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.281 { + iface MIXER + name 'pcm0_out mix 0 media_loop2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.282 { + iface MIXER + name 'pcm0_out mix 0 sidetone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.283 { + iface MIXER + name 'pcm0_out mix 0 txspeech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.284 { + iface MIXER + name 'pcm0_out mix 0 speech_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.285 { + iface MIXER + name 'pcm0_out mix 0 tone_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.286 { + iface MIXER + name 'pcm0_out mix 0 voip_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.287 { + iface MIXER + name 'pcm0_out mix 0 pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.288 { + iface MIXER + name 'pcm0_out mix 0 pcm1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.289 { + iface MIXER + name 'pcm0_out mix 0 low_pcm0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.290 { + iface MIXER + name 'pcm0_out mix 0 fm_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.291 { + iface MIXER + name 'media1_out mix 0 media0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.292 { + iface MIXER + name 'media1_out mix 0 media1_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.293 { + iface MIXER + name 'media1_out mix 0 media2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.294 { + iface MIXER + name 'media1_out mix 0 media3_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.295 { + iface MIXER + name 'media0_out mix 0 media0_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.296 { + iface MIXER + name 'media0_out mix 0 media1_in' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.297 { + iface MIXER + name 'media0_out mix 0 media2_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.298 { + iface MIXER + name 'media0_out mix 0 media3_in' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.299 { + iface MIXER + name 'media0_in gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.300 { + iface MIXER + name 'media0_in gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.301 { + iface MIXER + name 'media0_in gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.302 { + iface MIXER + name 'media1_in gain 0 rampduration' + value 50 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.303 { + iface MIXER + name 'media1_in gain 0 mute' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.304 { + iface MIXER + name 'media1_in gain 0 volume' + value.0 0 + value.1 0 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 0 + dbvalue.1 0 + } + } + control.305 { + iface MIXER + name 'media2_in gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.306 { + iface MIXER + name 'media2_in gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.307 { + iface MIXER + name 'media2_in gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.308 { + iface MIXER + name 'media3_in gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.309 { + iface MIXER + name 'media3_in gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.310 { + iface MIXER + name 'media3_in gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.311 { + iface MIXER + name 'pcm0_in gain 0 rampduration' + value 50 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.312 { + iface MIXER + name 'pcm0_in gain 0 mute' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.313 { + iface MIXER + name 'pcm0_in gain 0 volume' + value.0 0 + value.1 0 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 0 + dbvalue.1 0 + } + } + control.314 { + iface MIXER + name 'pcm1_in gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.315 { + iface MIXER + name 'pcm1_in gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.316 { + iface MIXER + name 'pcm1_in gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.317 { + iface MIXER + name 'low_pcm0_in gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.318 { + iface MIXER + name 'low_pcm0_in gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.319 { + iface MIXER + name 'low_pcm0_in gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.320 { + iface MIXER + name 'pcm1_out gain 0 rampduration' + value 50 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.321 { + iface MIXER + name 'pcm1_out gain 0 mute' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.322 { + iface MIXER + name 'pcm1_out gain 0 volume' + value.0 0 + value.1 0 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 0 + dbvalue.1 0 + } + } + control.323 { + iface MIXER + name 'pcm2_out gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.324 { + iface MIXER + name 'pcm2_out gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.325 { + iface MIXER + name 'pcm2_out gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.326 { + iface MIXER + name 'voip_in gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.327 { + iface MIXER + name 'voip_in gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.328 { + iface MIXER + name 'voip_in gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.329 { + iface MIXER + name 'voip_out gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.330 { + iface MIXER + name 'voip_out gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.331 { + iface MIXER + name 'voip_out gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.332 { + iface MIXER + name 'tone_in gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.333 { + iface MIXER + name 'tone_in gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.334 { + iface MIXER + name 'tone_in gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.335 { + iface MIXER + name 'aware_out gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.336 { + iface MIXER + name 'aware_out gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.337 { + iface MIXER + name 'aware_out gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.338 { + iface MIXER + name 'vad_out gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.339 { + iface MIXER + name 'vad_out gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.340 { + iface MIXER + name 'vad_out gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.341 { + iface MIXER + name 'hf_sns_out gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.342 { + iface MIXER + name 'hf_sns_out gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.343 { + iface MIXER + name 'hf_sns_out gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.344 { + iface MIXER + name 'hf_out gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.345 { + iface MIXER + name 'hf_out gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.346 { + iface MIXER + name 'hf_out gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.347 { + iface MIXER + name 'speech_out gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.348 { + iface MIXER + name 'speech_out gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.349 { + iface MIXER + name 'speech_out gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.350 { + iface MIXER + name 'txspeech_in gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.351 { + iface MIXER + name 'txspeech_in gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.352 { + iface MIXER + name 'txspeech_in gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.353 { + iface MIXER + name 'rxspeech_out gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.354 { + iface MIXER + name 'rxspeech_out gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.355 { + iface MIXER + name 'rxspeech_out gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.356 { + iface MIXER + name 'speech_in gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.357 { + iface MIXER + name 'speech_in gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.358 { + iface MIXER + name 'speech_in gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.359 { + iface MIXER + name 'codec_in0 gain 0 rampduration' + value 50 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.360 { + iface MIXER + name 'codec_in0 gain 0 mute' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.361 { + iface MIXER + name 'codec_in0 gain 0 volume' + value.0 0 + value.1 0 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 0 + dbvalue.1 0 + } + } + control.362 { + iface MIXER + name 'codec_in1 gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.363 { + iface MIXER + name 'codec_in1 gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.364 { + iface MIXER + name 'codec_in1 gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.365 { + iface MIXER + name 'codec_out0 gain 0 rampduration' + value 50 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.366 { + iface MIXER + name 'codec_out0 gain 0 mute' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.367 { + iface MIXER + name 'codec_out0 gain 0 volume' + value.0 0 + value.1 0 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 0 + dbvalue.1 0 + } + } + control.368 { + iface MIXER + name 'codec_out1 gain 0 rampduration' + value 50 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.369 { + iface MIXER + name 'codec_out1 gain 0 mute' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.370 { + iface MIXER + name 'codec_out1 gain 0 volume' + value.0 0 + value.1 0 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 0 + dbvalue.1 0 + } + } + control.371 { + iface MIXER + name 'bt_out gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.372 { + iface MIXER + name 'bt_out gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.373 { + iface MIXER + name 'bt_out gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.374 { + iface MIXER + name 'fm_out gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.375 { + iface MIXER + name 'fm_out gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.376 { + iface MIXER + name 'fm_out gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.377 { + iface MIXER + name 'bt_in gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.378 { + iface MIXER + name 'bt_in gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.379 { + iface MIXER + name 'bt_in gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.380 { + iface MIXER + name 'fm_in gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.381 { + iface MIXER + name 'fm_in gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.382 { + iface MIXER + name 'fm_in gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.383 { + iface MIXER + name 'modem_in gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.384 { + iface MIXER + name 'modem_in gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.385 { + iface MIXER + name 'modem_in gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.386 { + iface MIXER + name 'modem_out gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.387 { + iface MIXER + name 'modem_out gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.388 { + iface MIXER + name 'modem_out gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.389 { + iface MIXER + name 'media_loop1_out gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.390 { + iface MIXER + name 'media_loop1_out gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.391 { + iface MIXER + name 'media_loop1_out gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.392 { + iface MIXER + name 'media_loop2_out gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.393 { + iface MIXER + name 'media_loop2_out gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.394 { + iface MIXER + name 'media_loop2_out gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.395 { + iface MIXER + name 'sprot_loop_out gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.396 { + iface MIXER + name 'sprot_loop_out gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.397 { + iface MIXER + name 'sprot_loop_out gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.398 { + iface MIXER + name 'media0_in volume 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.399 { + iface MIXER + name 'media0_in volume 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.400 { + iface MIXER + name 'media0_in volume 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.401 { + iface MIXER + name 'sidetone_in gain 0 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.402 { + iface MIXER + name 'sidetone_in gain 0 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.403 { + iface MIXER + name 'sidetone_in gain 0 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.404 { + iface MIXER + name 'speech_out gain 1 rampduration' + value 5 + comment { + access 'read write' + type INTEGER + count 1 + range '5 - 5000' + } + } + control.405 { + iface MIXER + name 'speech_out gain 1 mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.406 { + iface MIXER + name 'speech_out gain 1 volume' + value.0 -1440 + value.1 -1440 + comment { + access 'read write' + type INTEGER + count 2 + range '-1440 - 360' + dbmin -14400 + dbmax 3600 + dbvalue.0 -14400 + dbvalue.1 -14400 + } + } + control.407 { + iface MIXER + name 'media_loop1_out fir 0 params' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 138 + } + } + control.408 { + iface MIXER + name 'media_loop1_out iir 0 params' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 300 + } + } + control.409 { + iface MIXER + name 'media_loop1_out mdrp 0 params' + value '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 76 + } + } + control.410 { + iface MIXER + name 'media_loop2_out fir 0 params' + value '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 272 + } + } + control.411 { + iface MIXER + name 'media_loop2_out iir 0 params' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 300 + } + } + control.412 { + iface MIXER + name 'media_loop2_out mdrp 0 params' + value '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 76 + } + } + control.413 { + iface MIXER + name 'aware_out fir 0 params' + value '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 272 + } + } + control.414 { + iface MIXER + name 'aware_out iir 0 params' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 300 + } + } + control.415 { + iface MIXER + name 'aware_out aware 0 params' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 48 + } + } + control.416 { + iface MIXER + name 'vad_out fir 0 params' + value '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 272 + } + } + control.417 { + iface MIXER + name 'vad_out iir 0 params' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 300 + } + } + control.418 { + iface MIXER + name 'sprot_loop_out lpro 0 params' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 192 + } + } + control.419 { + iface MIXER + name 'codec_in0 dcr 0 params' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 300 + } + } + control.420 { + iface MIXER + name 'codec_in1 dcr 0 params' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 300 + } + } + control.421 { + iface MIXER + name 'speech_out ul_module 0 params fir_speech' + value '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 136 + } + } + control.422 { + iface MIXER + name 'speech_out ul_module 0 params fir_hf_sns' + value '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 136 + } + } + control.423 { + iface MIXER + name 'speech_out ul_module 0 params iir_speech' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 48 + } + } + control.424 { + iface MIXER + name 'speech_out ul_module 0 params iir_hf_sns' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 48 + } + } + control.425 { + iface MIXER + name 'speech_out ul_module 0 params aec' + value '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 640 + } + } + control.426 { + iface MIXER + name 'speech_out ul_module 0 params nr' + value '0000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 38 + } + } + control.427 { + iface MIXER + name 'speech_out ul_module 0 params agc' + value '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 58 + } + } + control.428 { + iface MIXER + name 'speech_out ul_module 0 params biquad' + value '00000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 22 + } + } + control.429 { + iface MIXER + name 'speech_out ul_module 0 params compr' + value '000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 36 + } + } + control.430 { + iface MIXER + name 'speech_out ul_module 0 params sns' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 324 + } + } + control.431 { + iface MIXER + name 'speech_out ul_module 0 params ser' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 42 + } + } + control.432 { + iface MIXER + name 'speech_out ul_module 0 params cni' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 48 + } + } + control.433 { + iface MIXER + name 'speech_out ul_module 0 params ref' + value '000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 24 + } + } + control.434 { + iface MIXER + name 'speech_out ul_module 0 params delay' + value '000000000000' + comment { + access 'read write' + type BYTES + count 6 + } + } + control.435 { + iface MIXER + name 'speech_out ul_module 0 params bmf' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 264 + } + } + control.436 { + iface MIXER + name 'speech_out ul_module 0 params dnr' + value '000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 18 + } + } + control.437 { + iface MIXER + name 'speech_in dl_module 0 params ana' + value '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 52 + } + } + control.438 { + iface MIXER + name 'speech_in dl_module 0 params fir' + value '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 136 + } + } + control.439 { + iface MIXER + name 'speech_in dl_module 0 params iir' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 48 + } + } + control.440 { + iface MIXER + name 'speech_in dl_module 0 params nr' + value '0000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 38 + } + } + control.441 { + iface MIXER + name 'speech_in dl_module 0 params biquad' + value '00000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 22 + } + } + control.442 { + iface MIXER + name 'speech_in dl_module 0 params compr' + value '000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 36 + } + } + control.443 { + iface MIXER + name 'speech_in dl_module 0 params cni' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 48 + } + } + control.444 { + iface MIXER + name 'speech_in dl_module 0 params bwx' + value '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 54 + } + } + control.445 { + iface MIXER + name 'speech_in dl_module 0 params gmm' + value '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 586 + } + } + control.446 { + iface MIXER + name 'speech_in dl_module 0 params glc' + value '000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 18 + } + } + control.447 { + iface MIXER + name 'codec_out interleaver slot 0' + value codec_out0_0 + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 none + item.1 codec_out0_0 + item.2 codec_out0_1 + item.3 codec_out1_0 + item.4 codec_out1_1 + } + } + control.448 { + iface MIXER + name 'codec_out interleaver slot 1' + value codec_out0_1 + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 none + item.1 codec_out0_0 + item.2 codec_out0_1 + item.3 codec_out1_0 + item.4 codec_out1_1 + } + } + control.449 { + iface MIXER + name 'codec_out interleaver slot 2' + value codec_out1_0 + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 none + item.1 codec_out0_0 + item.2 codec_out0_1 + item.3 codec_out1_0 + item.4 codec_out1_1 + } + } + control.450 { + iface MIXER + name 'codec_out interleaver slot 3' + value codec_out1_1 + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 none + item.1 codec_out0_0 + item.2 codec_out0_1 + item.3 codec_out1_0 + item.4 codec_out1_1 + } + } + control.451 { + iface MIXER + name 'codec_in deinterleaver codec_in0_0' + value 'slot 0' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 none + item.1 'slot 0' + item.2 'slot 1' + item.3 'slot 2' + item.4 'slot 3' + } + } + control.452 { + iface MIXER + name 'codec_in deinterleaver codec_in0_1' + value 'slot 1' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 none + item.1 'slot 0' + item.2 'slot 1' + item.3 'slot 2' + item.4 'slot 3' + } + } + control.453 { + iface MIXER + name 'codec_in deinterleaver codec_in1_0' + value 'slot 2' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 none + item.1 'slot 0' + item.2 'slot 1' + item.3 'slot 2' + item.4 'slot 3' + } + } + control.454 { + iface MIXER + name 'codec_in deinterleaver codec_in1_1' + value 'slot 3' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 none + item.1 'slot 0' + item.2 'slot 1' + item.3 'slot 2' + item.4 'slot 3' + } + } + control.455 { + iface MIXER + name 'domain voice mode 0' + value narrowband + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 narrowband + item.1 wideband + } + } + control.456 { + iface MIXER + name 'domain bt mode 0' + value narrowband + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 narrowband + item.1 wideband + } + } + control.457 { + iface MIXER + name 'sst debug byte control' + value '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type BYTES + count 512 + } + } + control.458 { + iface MIXER + name 'probe out0 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.459 { + iface MIXER + name 'probe out1 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.460 { + iface MIXER + name 'probe out2 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.461 { + iface MIXER + name 'probe out3 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.462 { + iface MIXER + name 'probe out4 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.463 { + iface MIXER + name 'probe out5 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.464 { + iface MIXER + name 'probe out6 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.465 { + iface MIXER + name 'probe out7 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.466 { + iface MIXER + name 'probe in0 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.467 { + iface MIXER + name 'probe in1 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.468 { + iface MIXER + name 'probe in2 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.469 { + iface MIXER + name 'probe in3 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.470 { + iface MIXER + name 'probe in4 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.471 { + iface MIXER + name 'probe in5 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.472 { + iface MIXER + name 'probe in6 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } + control.473 { + iface MIXER + name 'probe in7 connection' + value 'media0_in gain' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'media0_in gain' + item.1 'media1_in gain' + item.2 'media2_in gain' + item.3 'media3_in gain' + item.4 'pcm0_in gain' + item.5 'pcm1_in gain' + item.6 'pcm1_out gain' + item.7 'pcm2_out gain' + item.8 'voip_in gain' + item.9 'voip_out gain' + item.10 'aware_out gain' + item.11 'vad_out gain' + item.12 'hf_sns_out gain' + item.13 'hf_out gain' + item.14 'speech_out gain' + item.15 'txspeech_in gain' + item.16 'rxspeech_out gain' + item.17 'speech_in gain' + item.18 'media_loop1_out gain' + item.19 'media_loop2_out gain' + item.20 'tone_in gain' + item.21 'codec_out0 gain' + item.22 'codec_out1 gain' + item.23 'bt_out gain' + item.24 'fm_out gain' + item.25 'modem_out gain' + item.26 'codec_in0 gain' + item.27 'codec_in1 gain' + item.28 'bt_in gain' + item.29 'fm_in gain' + item.30 'modem_in gain' + } + } +} diff --git a/device-software/meta-edison-distro/recipes-multimedia/libav/libav_0.8.9.bbappend b/device-software/meta-edison-distro/recipes-multimedia/libav/libav_0.8.9.bbappend new file mode 100644 index 0000000..939e988 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-multimedia/libav/libav_0.8.9.bbappend @@ -0,0 +1,23 @@ +# Disable features that have potential commercial licensing restrictions +EXTRA_OECONF += "\ + --disable-encoder=libmp3lame \ + --disable-decoder=mp3 \ + --disable-decoder=mp3adu \ + --disable-decoder=mp3adufloat \ + --disable-decoder=mp3float \ + --disable-decoder=mp3on4 \ + --disable-decoder=mp3on4float \ + --disable-muxer=mp3 \ + --disable-demuxer=mp3 \ + --disable-bsf=mp3_header_decompress \ + --disable-bsf=mp3_header_compress \ + \ + --disable-encoder=mpeg2video \ + --disable-decoder=mpeg2video \ + --disable-hwaccel=mpeg2_vaapi\ + --disable-hwaccel=mpeg2_dxva2\ + --disable-muxer=mpeg2dvd \ + --disable-muxer=mpeg2svcd \ + --disable-muxer=mpeg2video \ + --disable-muxer=mpeg2vob \ +" diff --git a/device-software/meta-edison-distro/recipes-multimedia/mplayer/mplayer-common.bb b/device-software/meta-edison-distro/recipes-multimedia/mplayer/mplayer-common.bb new file mode 100644 index 0000000..889bb95 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-multimedia/mplayer/mplayer-common.bb @@ -0,0 +1,22 @@ +# Copyright Matthias Hentges (c) 2006 +# License: MIT (see COPYING.MIT) + +DESCRIPTION = "Preconfigured mplayer preferences" + +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://${COREBASE}/meta/COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420" + +PV = "0.0.1" + +SRC_URI = "file://mplayer.conf" + +# Yes, really /usr/etc!!! +do_install() { + install -d "${D}/usr${sysconfdir}/mplayer" + + install -m 0644 ${WORKDIR}/mplayer.conf "${D}/usr${sysconfdir}/mplayer" +} + +FILES_${PN} = "/usr${sysconfdir}/mplayer" + +inherit allarch diff --git a/device-software/meta-edison-distro/recipes-multimedia/mplayer/mplayer-common/mplayer.conf b/device-software/meta-edison-distro/recipes-multimedia/mplayer/mplayer-common/mplayer.conf new file mode 100644 index 0000000..37ad65f --- /dev/null +++ b/device-software/meta-edison-distro/recipes-multimedia/mplayer/mplayer-common/mplayer.conf @@ -0,0 +1,15 @@ + +# You probably shouldn't touch these +ac=mad, +ao=alsa, + +# Required on SL-Cxxxx for correct rotation in the *VT*, +# breaks rotation in X! +# vf=rotate=1 + +# Enable fullscreen display by default +# fs=true + +# Drop frames to keep audio and video in sync +framedrop=true + diff --git a/device-software/meta-edison-distro/recipes-multimedia/mplayer/mplayer2/cross.compile.codec-cfg.patch b/device-software/meta-edison-distro/recipes-multimedia/mplayer/mplayer2/cross.compile.codec-cfg.patch new file mode 100644 index 0000000..7b290b5 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-multimedia/mplayer/mplayer2/cross.compile.codec-cfg.patch @@ -0,0 +1,16 @@ +Upstream-Status: Pending +Signed-off-by: Martin Jansa + +diff --git a/Makefile b/Makefile +index 6013ca3..28c6383 100644 +--- a/Makefile ++++ b/Makefile +@@ -600,7 +602,7 @@ mplayer$(EXESUF): + $(CC) -o $@ $^ $(EXTRALIBS) + + codec-cfg$(EXESUF): codec-cfg.c codec-cfg.h +- $(HOST_CC) -O -DCODECS2HTML -I. -o $@ $< ++ $(BUILD_CC) -O -DCODECS2HTML -I. -Iffmpeg -o $@ $< + + codecs.conf.h: codec-cfg$(EXESUF) etc/codecs.conf + ./$^ > $@ diff --git a/device-software/meta-edison-distro/recipes-multimedia/mplayer/mplayer2_git.bb b/device-software/meta-edison-distro/recipes-multimedia/mplayer/mplayer2_git.bb new file mode 100644 index 0000000..820bffa --- /dev/null +++ b/device-software/meta-edison-distro/recipes-multimedia/mplayer/mplayer2_git.bb @@ -0,0 +1,161 @@ +DESCRIPTION = "Open Source multimedia player." +SECTION = "multimedia" +HOMEPAGE = "http://www.mplayerhq.hu/" +DEPENDS = "libtheora ffmpeg zlib libpng jpeg liba52 freetype fontconfig alsa-lib lzo ncurses lame pulseaudio \ + ${@base_conditional('ENTERPRISE_DISTRO', '1', '', 'libmad liba52 lame', d)}" + +RDEPENDS_${PN} = "mplayer-common" +PROVIDES = "mplayer" +RPROVIDES_${PN} = "mplayer" +RCONFLICTS_${PN} = "mplayer" + +LICENSE = "GPLv3" +LIC_FILES_CHKSUM = "file://LICENSE;md5=d32239bcb673463ab874e80d47fae504" + +SRC_URI = "git://repo.or.cz/mplayer.git;protocol=git;branch=master \ + file://cross.compile.codec-cfg.patch \ +" + +SRCREV = "e3f5043233336d8b4b0731c6a8b42a8fda5535ac" + +ARM_INSTRUCTION_SET = "arm" + +PV = "2.0+gitr${SRCPV}" +PR = "r8" + +PARALLEL_MAKE = "" + +S = "${WORKDIR}/git" + +FILES_${PN} = "${bindir}/mplayer ${libdir} /usr/etc/mplayer/" +CONFFILES_${PN} += "/usr/etc/mplayer/input.conf \ + /usr/etc/mplayer/example.conf \ + /usr/etc/mplayer/codecs.conf \ + " + +inherit autotools pkgconfig + +EXTRA_OECONF = " \ + --prefix=/usr \ + --mandir=${mandir} \ + --target=${SIMPLE_TARGET_SYS} \ + \ + --disable-fontconfig \ + --disable-libass \ + --disable-lirc \ + --disable-lircc \ + --disable-joystick \ + --disable-vm \ + --disable-xf86keysym \ + --disable-tv \ + --disable-tv-v4l1 \ + --disable-tv-v4l2 \ + --disable-tv-bsdbt848 \ + --enable-rtc \ + --disable-networking \ + --disable-smb \ + --disable-live \ + --disable-dvdnav \ + --disable-dvdread \ + --disable-dvdread-internal \ + --disable-libdvdcss-internal \ + --disable-cdparanoia \ + --enable-freetype \ + --disable-sortsub \ + --disable-fribidi \ + --disable-enca \ + --disable-ftp \ + --disable-vstream \ + \ + --disable-gif \ + --disable-png \ + --disable-jpeg \ + --disable-libcdio \ + --disable-qtx \ + --disable-xanim \ + --disable-real \ + --disable-xvid \ + \ + --disable-mpg123 \ + --disable-speex \ + --enable-theora \ + --disable-ladspa \ + --disable-libdv \ + --disable-mad \ + --disable-xmms \ + --disable-musepack \ + \ + --disable-gl \ + --disable-vesa \ + --disable-svga \ + --disable-sdl \ + --disable-aa \ + --disable-caca \ + --disable-ggi \ + --disable-ggiwmh \ + --disable-directx \ + --disable-dxr3 \ + --disable-dvb \ + --disable-mga \ + --disable-xmga \ + --disable-xv \ + --disable-vm \ + --disable-xinerama \ + --disable-x11 \ + --disable-fbdev \ + --disable-3dfx \ + --disable-tdfxfb \ + --disable-s3fb \ + --disable-directfb \ + --disable-bl \ + --disable-tdfxvid \ + --disable-tga \ + --disable-pnm \ + --disable-md5sum \ + \ + --disable-alsa \ + --disable-ossaudio \ + --disable-arts \ + --disable-esd \ + --enable-pulse \ + --disable-jack \ + --disable-openal \ + --disable-nas \ + --disable-sgiaudio \ + --disable-sunaudio \ + --disable-win32waveout \ + --enable-select \ + \ + --extra-libs=' -lstdc++ -lvorbis ' \ +" + +EXTRA_OECONF_append_armv6 = " --enable-armv6" +EXTRA_OECONF_append_armv7a = " --enable-armv6 --enable-neon" + +FULL_OPTIMIZATION = "-fexpensive-optimizations -fomit-frame-pointer -frename-registers -O4 -ffast-math" +BUILD_OPTIMIZATION = "${FULL_OPTIMIZATION}" + +CFLAGS_append = " -I${S}/libdvdread4 " + +do_configure() { + sed -i 's|/usr/include|${STAGING_INCDIR}|g' ${S}/configure + sed -i 's|/usr/lib|${STAGING_LIBDIR}|g' ${S}/configure + sed -i 's|/usr/\S*include[\w/]*||g' ${S}/configure + sed -i 's|/usr/\S*lib[\w/]*||g' ${S}/configure + sed -i 's|_install_strip="-s"|_install_strip=""|g' ${S}/configure + sed -i 's|HOST_CC|BUILD_CC|' ${S}/Makefile + + export SIMPLE_TARGET_SYS="$(echo ${TARGET_SYS} | sed s:${TARGET_VENDOR}::g)" + ./configure ${EXTRA_OECONF} +} + +do_compile () { + oe_runmake +} + +do_install_append() { + install -d ${D}/usr/etc/mplayer + install ${S}/etc/input.conf ${D}/usr/etc/mplayer/ + install ${S}/etc/example.conf ${D}/usr/etc/mplayer/ + install ${S}/etc/codecs.conf ${D}/usr/etc/mplayer/ +} diff --git a/device-software/meta-edison-distro/recipes-multimedia/pulseaudio/files/pulseaudio.service b/device-software/meta-edison-distro/recipes-multimedia/pulseaudio/files/pulseaudio.service new file mode 100644 index 0000000..0f43b1f --- /dev/null +++ b/device-software/meta-edison-distro/recipes-multimedia/pulseaudio/files/pulseaudio.service @@ -0,0 +1,12 @@ +[Unit] +Description=PulseAudio Sound System +After=alsa-restore.service + +[Service] +BusName=org.pulseaudio.Server +ExecStart=/usr/bin/pulseaudio --system --resample-method=src-sinc-fastest +Restart=always + +[Install] +WantedBy=multi-user.target + diff --git a/device-software/meta-edison-distro/recipes-multimedia/pulseaudio/files/system.pa b/device-software/meta-edison-distro/recipes-multimedia/pulseaudio/files/system.pa new file mode 100644 index 0000000..77e5a35 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-multimedia/pulseaudio/files/system.pa @@ -0,0 +1,66 @@ +#!/usr/bin/pulseaudio -nF +# +# This file is part of PulseAudio. +# +# PulseAudio is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# PulseAudio 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 Lesser General Public License +# along with PulseAudio; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +# This startup script is used only if PulseAudio is started in system +# mode. + +### Automatically load driver modules depending on the hardware available +.ifexists module-udev-detect.so +load-module module-udev-detect +.else +### Use the static hardware detection module (for systems that lack udev/hal support) +load-module module-detect +.endif + +### Load several protocols +.ifexists module-esound-protocol-unix.so +load-module module-esound-protocol-unix +.endif +load-module module-native-protocol-unix + +### Load bluetooth modules +.ifexists module-bluetooth-policy.so +load-module module-bluetooth-policy +.endif + +.ifexists module-bluez5-discover.so +load-module module-bluez5-discover +.endif + +### Automatically restore the volume of streams and devices +load-module module-stream-restore +load-module module-device-restore + +### Automatically restore the default sink/source when changed by the user +### during runtime +### NOTE: This should be loaded as early as possible so that subsequent modules +### that look up the default sink/source get the right value +load-module module-default-device-restore + +### Automatically move streams to the default sink if the sink they are +### connected to dies, similar for sources +load-module module-rescue-streams + +### Make sure we always have a sink around, even if it is a null sink. +load-module module-always-sink + +### Automatically suspend sinks/sources that become idle for too long +load-module module-suspend-on-idle + +### Enable positioned event sounds +load-module module-position-event-sounds \ No newline at end of file diff --git a/device-software/meta-edison-distro/recipes-multimedia/pulseaudio/pulseaudio-service_1.0.bb b/device-software/meta-edison-distro/recipes-multimedia/pulseaudio/pulseaudio-service_1.0.bb new file mode 100644 index 0000000..e8e10b5 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-multimedia/pulseaudio/pulseaudio-service_1.0.bb @@ -0,0 +1,28 @@ +DESCRIPTION = "Pulseaudio systemd service" +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" + +inherit systemd useradd + +DEPENDS = "pulseaudio" + +SRC_URI += "\ + file://pulseaudio.service \ + " + +SYSTEMD_SERVICE_${PN} = "pulseaudio.service" + +FILES_${PN} = " \ + ${systemd_unitdir} \ +" + +USERADD_PACKAGES = "pulseaudio-service" +GROUPMEMS_PARAM_pulseaudio-service = " --add root --group audio" + +do_install_append() { + if ${@base_contains('DISTRO_FEATURES','systemd','true','false',d)}; then + install -d ${D}${systemd_unitdir}/system + install -m 0644 ${WORKDIR}/pulseaudio.service ${D}/${systemd_unitdir}/system/ + fi +} + diff --git a/device-software/meta-edison-distro/recipes-multimedia/pulseaudio/pulseaudio_5.0.bbappend b/device-software/meta-edison-distro/recipes-multimedia/pulseaudio/pulseaudio_5.0.bbappend new file mode 100644 index 0000000..7900344 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-multimedia/pulseaudio/pulseaudio_5.0.bbappend @@ -0,0 +1,22 @@ +# Overlay the pulseaudio recipe to embed bluetooth modules for A2DP + +FILESEXTRAPATHS_prepend := "${THISDIR}/files/" + +SRC_URI += "\ + file://system.pa \ + " + +PACKAGECONFIG ??= "${@base_contains('DISTRO_FEATURES', 'bluetooth', 'bluez5', '', d)} \ + ${@base_contains('DISTRO_FEATURES', 'systemd', 'systemd', '', d)} \ + ${@base_contains('DISTRO_FEATURES', 'zeroconf', 'avahi', '', d)} \ + ${@base_contains('DISTRO_FEATURES', 'x11', 'x11', '', d)}" + +RDEPENDS_pulseaudio-server += " \ + pulseaudio-module-loopback \ + pulseaudio-module-bluez5-discover \ + pulseaudio-module-bluez5-device \ + pulseaudio-module-bluetooth-policy" + +do_install_append() { + install -m 0644 ${WORKDIR}/system.pa ${D}/${sysconfdir}/pulse/ +} diff --git a/device-software/meta-edison-distro/recipes-support/blink-led/blink-led_0.1.bb b/device-software/meta-edison-distro/recipes-support/blink-led/blink-led_0.1.bb new file mode 100644 index 0000000..9fff7d2 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-support/blink-led/blink-led_0.1.bb @@ -0,0 +1,23 @@ +DESCRIPTION = "Blinks the Edison LED" +SECTION = "base" +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" + +FILESEXTRAPATHS_prepend := "${THISDIR}/files/" + +SRC_URI = "file://blink-led" +SRC_URI += "file://blink-led.service" + +S = "${WORKDIR}" + +do_install() { + install -d ${D}${bindir} + install -m 0755 blink-led ${D}${bindir} + + # Copy service file + install -d ${D}/${systemd_unitdir}/system + install -m 644 ${WORKDIR}/blink-led.service ${D}/${systemd_unitdir}/system +} + +FILES_${PN} += "${base_libdir}/systemd/system/blink-led.service" +FILES_${PN} += "${bindir}/blink-led" diff --git a/device-software/meta-edison-distro/recipes-support/blink-led/files/blink-led b/device-software/meta-edison-distro/recipes-support/blink-led/files/blink-led new file mode 100755 index 0000000..f8b884f --- /dev/null +++ b/device-software/meta-edison-distro/recipes-support/blink-led/files/blink-led @@ -0,0 +1,109 @@ +#!/usr/bin/env python + +# +# Edison LED blinker +# +# Copyright (c) 2014, Intel Corporation. +# Fabien Chereau +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +# This is a very poor implementation as it uses subprocesses for init + +import subprocess +import time +import argparse +import signal +import sys + +initial_led_state = "high" + +def write_led(value): + with open("/sys/class/gpio/gpio40/direction","w") as lf: + lf.write(value) + +def blink_once(): + write_led("low") + time.sleep(1./args.frequency*args.duty_cycle) + write_led("high") + time.sleep(1./args.frequency*(1.-args.duty_cycle)) + +def deinit_led_gpio(): + # Revert to default state with LED activated + write_led(initial_led_state) + + # Deinit LED GPIO + subprocess.call(""" + echo 40 >/sys/class/gpio/unexport + echo 214 >/sys/class/gpio/unexport + echo 243 >/sys/class/gpio/unexport + echo 261 >/sys/class/gpio/unexport + """, shell=True) + +def signal_term_handler(signal, frame): + print 'Signal intercepted: de-initing LED GPIOs' + deinit_led_gpio() + sys.exit(0) + + +parser = argparse.ArgumentParser(description='Blink the Edison Arduino board LED.') +parser.add_argument('--frequency', type=float, default=4, help='blink frequency in Hz') +parser.add_argument('--duration', type=float, default=-1, help='duration of the blink in seconds. Negative value means no timeout, i.e. it will stop when the program is killed.') +parser.add_argument('--duty_cycle', type=float, default=0.5, help='duty cycle between 0 and 1') + +args = parser.parse_args() + +# Allows to quit cleanly with CRTL+C or SIGTERM (systemd use SIGTERM to kill a service by default) +signal.signal(signal.SIGTERM, signal_term_handler) +signal.signal(signal.SIGINT, signal_term_handler) + +# Init GPIO mux for LED control +subprocess.call(""" +echo 40 >/sys/class/gpio/export +echo 214 >/sys/class/gpio/export +echo 243 >/sys/class/gpio/export +echo 261 >/sys/class/gpio/export +echo high >/sys/class/gpio/gpio214/direction +echo mode0 > /sys/kernel/debug/gpio_debug/gpio40/current_pinmux +echo low >/sys/class/gpio/gpio243/direction +echo high >/sys/class/gpio/gpio261/direction +echo low >/sys/class/gpio/gpio214/direction""", shell=True) + +# Save current LED value for reverting to proper state at exit +try: + lf = open("/sys/class/gpio/gpio40/value","r") + v = lf.read() + lf.close() + if v[0] == '0': + initial_led_state = "low" + else: + initial_led_state = "high" +except: + print "Can't get current LED state" + +# Blink LED +if args.duration >= 0: + for i in range(0, int(args.duration*args.frequency)): + blink_once() +else: + while True: + blink_once() + +deinit_led_gpio() + diff --git a/device-software/meta-edison-distro/recipes-support/blink-led/files/blink-led.service b/device-software/meta-edison-distro/recipes-support/blink-led/files/blink-led.service new file mode 100644 index 0000000..3dcb2ab --- /dev/null +++ b/device-software/meta-edison-distro/recipes-support/blink-led/files/blink-led.service @@ -0,0 +1,10 @@ +[Unit] +Description=Edison Arduino board LED Blinker + +[Service] +ExecStart=/usr/bin/blink-led +Restart=on-failure + +[Install] +WantedBy=basic.target + diff --git a/device-software/meta-edison-distro/recipes-support/cleanjournal/cleanjournal.bb b/device-software/meta-edison-distro/recipes-support/cleanjournal/cleanjournal.bb new file mode 100644 index 0000000..cc21638 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-support/cleanjournal/cleanjournal.bb @@ -0,0 +1,31 @@ +DESCRIPTION = "Cleanjournal tool. Remove all corrupted journald entries at startup." +SECTION = "base" +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" + + +SRC_URI += "file://cleanjournal.service" +SRC_URI += "file://clean_journal.sh" + +SYSTEMD_SERVICE_${PN} = "cleanjournal.service" + +RDEPENDS_${PN} = "systemd" +DEPENDS = "systemd" +inherit systemd + +do_install() { + # install service file + install -d ${D}${systemd_unitdir}/system + install -c -m 0644 ${WORKDIR}/cleanjournal.service ${D}${systemd_unitdir}/system + + # install cleanjournal script + install -d ${D}${sbindir} + install -c -m 0755 ${WORKDIR}/clean_journal.sh ${D}${sbindir} +} + +# As this package is tied to systemd, only build it when we're also building systemd. +python () { + if not oe.utils.contains ('DISTRO_FEATURES', 'systemd', True, False, d): + raise bb.parse.SkipPackage("'systemd' not in DISTRO_FEATURES") +} + diff --git a/device-software/meta-edison-distro/recipes-support/cleanjournal/files/clean_journal.sh b/device-software/meta-edison-distro/recipes-support/cleanjournal/files/clean_journal.sh new file mode 100644 index 0000000..5758d09 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-support/cleanjournal/files/clean_journal.sh @@ -0,0 +1,60 @@ +#!/bin/sh + +# +# Cleanjournal script +# +# Copyright (c) 2014, Intel Corporation. +# Fabien Rodriguez +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +# +# This script checks if the remaining space in root partition is lower than 10%. +# In that case, the corrupted journald entries (ended by '~') will be deleted +# one by one until remaining space becomes greater or equal to 10%. +# At each boot, the script is launched by systemd at startup. +# + +# max allowed free space is 10% +max_allowed_free_space=10 + +# check remaining space and update clean_journal_needed variable +check_free_space() { + current_free_space=$(df -h | grep /dev/root | awk '{print 100 - $5}' | sed 's/%//') + if [ "$current_free_space" -lt "$max_allowed_free_space" ]; then + clean_journal_needed=true + else + clean_journal_needed=false + fi +} + +check_free_space +if [ "$clean_journal_needed" = true ]; then + # delete each journald corrupted entry + # until remaining space becomes greater than 10% + for corrupted_journal_file in $(find /var/log/journal/ -name '*~'); do + rm "$corrupted_journal_file" + check_free_space + if [ "$clean_journal_needed" = false ]; then + break + fi + done +fi + diff --git a/device-software/meta-edison-distro/recipes-support/cleanjournal/files/cleanjournal.service b/device-software/meta-edison-distro/recipes-support/cleanjournal/files/cleanjournal.service new file mode 100644 index 0000000..ab704f3 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-support/cleanjournal/files/cleanjournal.service @@ -0,0 +1,10 @@ +[Unit] +Description=Cleanjournal service + +[Service] +ExecStart=/usr/sbin/clean_journal.sh +Restart=no + +[Install] +WantedBy=basic.target + diff --git a/device-software/meta-edison-distro/recipes-support/crashlog/files/retrieve_crashlog.sh b/device-software/meta-edison-distro/recipes-support/crashlog/files/retrieve_crashlog.sh index dcb987c..a7f007c 100755 --- a/device-software/meta-edison-distro/recipes-support/crashlog/files/retrieve_crashlog.sh +++ b/device-software/meta-edison-distro/recipes-support/crashlog/files/retrieve_crashlog.sh @@ -34,32 +34,65 @@ # ipanic_console_path=/proc/emmc_ipanic_console -crashlog_path=/home/root/ +crashlog_path=/home/root # line containing 'WAKESRC' looks like: -# '[ 0.530153] [BOOT] WAKESRC=[real reset] (osnib)' -# wakesrc is the 6th field with [ and ] separators +# 'Jan 01 00:00:12 edison kernel: [BOOT] WAKESRC=[real reset] (osnib)' +# wakesrc is the 4th field with [ and ] separators # List of available wake sources is in driver/platform/x86/intel_scu_ipcutil.c -wakesrc=`dmesg | grep WAKESRC | awk -F'[][]' '{print $6}'` +wakesrc=$(journalctl -k -b -0 | grep WAKESRC | awk -F'[][]' '{print $4}') # any watchdog boot implies a crash -tmp=`echo -n "${wakesrc}" | grep watchdog` +tmp=$(echo -n "${wakesrc}" | grep watchdog) if [ -n "${tmp}" ]; then - # get the last sequence number (ie for crashlog_00001, get the 1) - last_file_sequence_number=`ls ${crashlog_path}/crashlog_* | tail -1 | awk -F_ '{print $NF}'` - if [ -z $last_file_sequence_number ]; then - last_file_sequence_number="0" - fi - - new_file_sequence_number=`expr ${last_file_sequence_number} + 1` - new_file_name=`printf "${crashlog_path}/crashlog_%05d\n" $new_file_sequence_number` - echo "****** Wake Source is [ ${wakesrc} ] ******" > ${new_file_name} - - if [ -e ${ipanic_console_path} ]; then - # we got an epanic trace - standard case - cat ${ipanic_console_path} >> ${new_file_name} - echo clear > ${ipanic_console_path} - fi + # get the last sequence number (ie for crashlog_00001, get the 1) + last_sequence_number=$(ls ${crashlog_path}/crashlog_* | tail -1 | awk -F_ '{print $NF}' | awk -F. '{print $NR}') + if [ -z $last_sequence_number ]; then + last_sequence_number="0" + fi + + new_sequence_number=$(expr ${last_sequence_number} + 1) + new_name=$(printf "crashlog_%05d" $new_sequence_number) + + # create working directory + mkdir ${crashlog_path}/${new_name} + + # write crashfile + crashfile_path=${crashlog_path}/${new_name}/crashfile + + event="CRASH" + manufacturer="Intel Corporation" + product_name=$(cat /factory/hardware_model) + version=$(cat /factory/hardware_version) + serial_number=$(cat /factory/serial_number) + linux_version=$(uname -a) + build_version=$(cat /etc/version) + date=$(date) + + echo "EVENT=${event}" > ${crashfile_path} + echo "Manufacturer : ${manufacturer}" >> ${crashfile_path} + echo "Product name : ${product_name}" >> ${crashfile_path} + echo "Version : ${version}" >> ${crashfile_path} + echo "Serial Number : ${serial_number}" >> ${crashfile_path} + echo "Linux version : ${linux_version}" >> ${crashfile_path} + echo "Build version : ${build_version}" >> ${crashfile_path} + echo "Date : ${date}" >> ${crashfile_path} + echo -e "Wake source : ${wakesrc}" >> ${crashfile_path} + + # write full journal binary & logs from previous boot + journalctl -b -1 -o short-monotonic > ${crashlog_path}/${new_name}/journal_logs + journalctl -b -1 -o export > ${crashlog_path}/${new_name}/journal_binary + + # write panic trace + if [ -e ${ipanic_console_path} ]; then + cat ${ipanic_console_path} > ${crashlog_path}/${new_name}/panic + echo clear > ${ipanic_console_path} + fi + + # create archive and clear folder + tar -zcf ${crashlog_path}/${new_name}.tar.gz -C ${crashlog_path} ${new_name} + rm -rf ${crashlog_path}/${new_name} + fi diff --git a/device-software/meta-edison-distro/recipes-support/edison-mcu/files/intel_mcu.bin b/device-software/meta-edison-distro/recipes-support/edison-mcu/files/intel_mcu.bin new file mode 100644 index 0000000000000000000000000000000000000000..dcf054447947db9cd557fe5abd56cda525f56861 GIT binary patch literal 12212 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 literal 0 HcmV?d00001 diff --git a/device-software/meta-edison-distro/recipes-support/edison-mcu/files/mcu_fw_loader.service b/device-software/meta-edison-distro/recipes-support/edison-mcu/files/mcu_fw_loader.service new file mode 100644 index 0000000..0057ab6 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-support/edison-mcu/files/mcu_fw_loader.service @@ -0,0 +1,10 @@ +[Unit] +Description=Daemon to load edison mcu app binary +After=syslog.target + +[Service] +ExecStart=/etc/intel_mcu/mcu_fw_loader.sh + +[Install] +WantedBy=multi-user.target + diff --git a/device-software/meta-edison-distro/recipes-support/edison-mcu/files/mcu_fw_loader.sh b/device-software/meta-edison-distro/recipes-support/edison-mcu/files/mcu_fw_loader.sh new file mode 100644 index 0000000..734352d --- /dev/null +++ b/device-software/meta-edison-distro/recipes-support/edison-mcu/files/mcu_fw_loader.sh @@ -0,0 +1,12 @@ +#!/bin/sh +#author: JiuJin Hong (jiujinx.hong@intel.com) +if [ ! -d "/sys/devices/platform/intel_mcu" ];then + exit +fi + +if [ ! -f "/lib/firmware/intel_mcu.bin" ];then + exit +fi + +echo "load mcu app" > /sys/devices/platform/intel_mcu/control + diff --git a/device-software/meta-edison-distro/recipes-support/edison-mcu/mcu-fw-bin_0.1.bb b/device-software/meta-edison-distro/recipes-support/edison-mcu/mcu-fw-bin_0.1.bb new file mode 100644 index 0000000..eafb813 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-support/edison-mcu/mcu-fw-bin_0.1.bb @@ -0,0 +1,17 @@ +DESCRIPTION = "This is edison mcu fw binary." +HOMEPAGE = "http://www.intel.com" +LICENSE = "CLOSED" + +FILESEXTRAPATHS_prepend := "${THISDIR}/files/" + +SRC_URI = "file://intel_mcu.bin" + +S = "${WORKDIR}" + +do_install () { + install -v -d ${D}/${base_libdir}/firmware/ + install -m 644 intel_mcu.bin ${D}/${base_libdir}/firmware/ +} + +FILES_${PN} = "${base_libdir}/firmware/" + diff --git a/device-software/meta-edison-distro/recipes-support/edison-mcu/mcu-fw-load_0.1.bb b/device-software/meta-edison-distro/recipes-support/edison-mcu/mcu-fw-load_0.1.bb new file mode 100644 index 0000000..a48aa10 --- /dev/null +++ b/device-software/meta-edison-distro/recipes-support/edison-mcu/mcu-fw-load_0.1.bb @@ -0,0 +1,23 @@ +DESCRIPTION = "This is intel mcu app download daemon." +HOMEPAGE = "http://www.intel.com" +LICENSE = "CLOSED" + +FILESEXTRAPATHS_prepend := "${THISDIR}/files/" + +SRC_URI = "file://mcu_fw_loader.service \ + file://mcu_fw_loader.sh" + +SYSTEMD_SERVICE_${PN} = "mcu_fw_loader.service" + +S = "${WORKDIR}" + +inherit systemd + +do_install () { + install -d ${D}${sysconfdir}/intel_mcu/ + install -m 0755 mcu_fw_loader.sh ${D}${sysconfdir}/intel_mcu/ + + install -d ${D}${systemd_unitdir}/system/ + install -m 0644 mcu_fw_loader.service ${D}${systemd_unitdir}/system/ +} + diff --git a/device-software/meta-edison-distro/recipes-support/edison-sst/files/fw_sst_119a.bin b/device-software/meta-edison-distro/recipes-support/edison-sst/files/fw_sst_119a.bin new file mode 100644 index 0000000000000000000000000000000000000000..b704fb62c6159e47614dc72450b3bf133d22879d GIT binary patch literal 464927 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

  • 1yAzaAiY15{`#qZGVsNFGG<-vbJH^WqaUzgSJAfLl+NJl8i}HLcxV#cT z=Webc4ok1rsB*%D)enT)o0o`h5RLS;S8}ZT3io@zIm%Zo_nRYq9`(>yOop;mR{17r zBk2Xf<&ZlkvDiGvE%ZBt3&(_jnev;llG6OV#O}yg18V3hmi9z!Zs)j$hgd_8@YB^l zXRvvGp==UcmZU68qFQB-m6l&+wjL7raheR)k+SXE7)E+hnBo>V3HY)oij!r^wG!tz zP1Zr26S*sKIL{~64Z{IHFgWpp49O`0Z=U8Q3cEyJ;-Jv%;IQ)DH>?k=FY45npJS`D z#qoxC1O>au+7C%w5~))n^BTZUVSRHGy>s=}mI!yZ$nKeI^3FB1wdnEa@@TwsjqrHq zCfF?@&SK6>7V`2U)*D6bT$EHjFFQRTp#!;$KMGB%JMX^-o!S_m%Bj{^jl6`;{=y#0 zWPhjI|9Y+3A4dBZGWbvhiOVJLJ=Ed18JKqn&ILn%uxd4+(_|Ejm5UA5&qK9e6OPcF zyyy=#+zA+c+E?4#RI4bk)hThdQKa0-%B^eLYVNPGvkL2?wvx_akX)dul4eSG_87iL zs!mqr@=Wu3X81g_8s27ipEH~d=7rf}n$Duz8*U?#4m9G>mkc?%&&#To9(|_a112a$ zK&$!C8vyDS#%X~SIK5XQF{J|wJwU_9g_ftW7)N4xtur9sJOmk}B~zG>d$%cF&`2Q{ z?H1>%=ZavsA^=x%rPIjLi{rkokU<51`z2vW&jLfVWwi%{V;6-$OiH}MpK(hKaQ?+9 zu*5e0313O1sW^<=WSSAqZEAo|B-S0stEddJNZiFjka1zA5aZVH;wwbV(;8y>OlJ65 z;8G7To3jA1`lLHI)qgHjo*zZ#B((fLncY_b5snk%cINK|Yygx5!GWAxaez6(mYy#5PCFyfXX%ga6_R3C@?b>G zkSEQY=LCvRaLEy$<13K6ci0&j|0ZF1@N?+E9L5g=eRHUzb!vB=x~)!cpCd~#1SkCF z)6G*b-%}eJ#Ll!c3a3NZ6`MqgWTXH*wPmA7Jny35VnY3ZN7xq_0T$$8-AdAGoYF8L zNMQjFor^Kevy7ji0u1fUV0|pxXLRJ(5e=?T(8l3&{;tAzHrN9&&k z{f@SfxJp>q-1Fsv|G;`n80;>=rv8gs%pIWM=$k)>lkP0ZKTQ%SXV4 z?+{GFTEo4Jd`+{8SJKX2=5P!H7?d<8dugK{0P++)vZuzSqRRga%B@ve`4AaJ+LvkM zR$(E-gmBgYk@o^3$Lkt43q+vuOLnV26EN*_li1uuZEhlkB(@qhbL9twth^}j z#?aRs6YQ;|*)*oV2c{@rFFz{)v2}U4(;dJJCFL{5<~ErpWD?ba-+CK*N>79y1RMDTV^8O-n`BF5P;Y+)M8@4`er`p0J$t1kBTT z3uIIBfH|eP*h^&KyCHh|R~h({P`gpy(VmH81s_P~(4Df4G71uJZN`n?%Da0x zCw;4P4idrCCQ~(vDg!WNXRHDgU%6h}LUntBawEXjTmu(y&_IsSuQM)ks`-hf2|x0N zsF&ZBce8zTR`(0sr-oY6h|a_FY>XES<*pMNKD|ChTigHMwL z43R}PH@{Ni&yZX_e|BMag^O9-yMJ%R5G6uF(viR6!3SmQTQ>=IiyG4EWupDr-@KpE ze1Y3izd>j|&1IKc#J3R7VjPUk9pGc;bz+qrVIzB&)s!bIUMVa#6>V^r4#?{F{5*6y)Q8BstlkWY z&tO~`5_<+aXtSQ%zjt9(MsMBgwRBHL`K8cgX3{n%sfgF4RR5=ymr<^C%9k#&r_)y9 zm@}QdS~qgaeWI`~eqYP3I(lz>{m!3F7ennb%2p2IdAqHu+%ysjI%^)aIHc%Zw^n!A z7b5R2fi2I^AN;PnLHqpZaG@>U>G73FHgqlUK;t^_D?QLChl6(5;}S7Dbb#- zM)5v^iI%>6J*Z2?`~DDlV3#$%+yAk3aPXNkGnlTw3x-Fo6F7A9hq+|S#YAvAw)8LF z6)Q`&gO%XK40P-$|M^DplACc6uZ)o|cFl+ad+p9-TW-_bj>IqLXZJQqdggXd%ig~? zv8Dg;j&aS28zofcIkztn+nx3PM- zP|6r8$ql6|LPPja#v95yLuK|*!uJ7at^rUMMPcx-YSHK1^y^#n;0pB~ge+=0BT*wYMyt z>(<$pu5}OJ?oEj5NjvxL+1vfbsNODDpZwgn{<5f&glNdRE0$@NE^?PFi!Oa#qN}T^ zc_#C0TS*ud2xBY4n6fYomI&|)aa}52i-1Eci}Hp^D#B!eFsVN*#JVsmz4#UMF&{IOhb!eu>*kv zzn_($IpM*hcbUe^(s=7)Dfi7a(YY+@W|B9D4Rsq$S1|H2qv|HLb+0K>W_QrMgKEmH zFc;6H9aZajbII<3fskR7-xAq@RXOqqIXx}&{?7XT!1dBexsExzdvNBi0%@! zav<7=$am)SFOw2H6MN9|l5Y+|E1NJ!VGBKZ5Ilw)l(J#sEFU$sSWoV(d79eb*8=T*to z&yu>I&H!;b@Md>k)56*>F3&!^FT?@a+RU#>%^Uo}_h7JyH5W$^MdbiDL7Nwkf|`@y zOxWCLDs9mujX0ACJx&J1Em(gMXG;7ipP-!75$#M+RZjdlh#>`!x#E);Wfs26{D~8%hszE{_OGKn<@W3sMHdYh?V1S2U=pMsW1%T?fgT;6qzUMrd4@N$xD?oM~L; z3Q~(QlO}m+>&Z(lHUw0wkCk~@>Hjz1nqIedR_L|&gKY6O7vHwu0q*hMN^i(wl*Qe` zH*X!95T-kKdF!>mgToBw^3l)CBLsf9NB+9a;pLkukM{GNs<)+bA;~uRr6-lmUg%?` zpujvE;x%cSh6VyIR&Hly0O|l1H8*uZ;D7wU^ir)9p&-;O2G)M0ZNoMpL-CB8cmQkgR5=s&xeTPO%I_0H@t~!lU ztx~Gm)PDb^CT~@l={u?Y&0!z2q{9U<>J6noE#g|gGfjO|7?VkiT<5P?&b2sKs(0;; zaQ}t(?~buq&YknGRByN841+*Iip?@l2=53%{v9uq{GH|WUJQRZ)bT!#XEuH*wEyms zA94`0+Gz|m4M|;u!>?)E$BV5G%q`gTUXQvwd-S^&XF|&KbtW&Z)>dD1c_J>nBOD`# zH}4p^s&;xbfn_OvbHs__@-kZ5B{&mOZ5FNa!pr+07bAkyREQU?M7Ek*+VTUUBLPpn z;6q`|5k1AGhv}}4AW(%2KD0gNlIcsSpIH(xM!QTD21VeL@Ly%E(fj^(hSX$ccHwI=I2liw}%>kk)Dg|p_p1K(usgWI#BN9;4N=KLx2h4 z7&Kv^pbZ8{{DQ+R;eLM>Du%SkVA0!GrtDr)c}np7L#Y3UV69TSZEA;2&96-PmoGCy zk)NHU1yP%wq=o5_j(SAFk)K$0NoBFf6;MiDsVeZiv@nvV+HF@gXcuE`l@o;7r<6l% zn%z(}Dk2`*Sk{?FCoM@k_MvbgR`92#I2~_!9U(osmbba|k@M=K|4=U{Eb(Y5fH3^<8m(u<$ZlK7RotAR&B9Sr2P-9gzc zlxx9m`-;?p_Ewr_7tQHxy5+kM%n^JXEuueWxQALBp`)w>arij>5s@Xd-xIe;WnbvVn>VB_| z(-ulw>26hz#B^SGNZA-y+ATRCb9&U>@#PnVT1kh(rQ?He-G_5Oz|5g`;O31JUlag7WDkG3{$LAtq?H6<^rG0 zW=^fKPHnp!rM=^Z(NW&SmS2R$)LZo1-K8f+I_`0zCN0-ysAfcM{7AqtH`3|U0B!WB z^)EYkOU6bOj_@%J3LW>fEZ42I>$rkNOZr~lg#&?|>9jL0#QO*}X~mZx<_*x$&nz&W ziJUfr>9_~+v?oR@YXw_!AS%TAC&x^=?$il-(hnzyeqUp(oC{gf2ky=QtwQj|5uyE0 z!qz_vM#bXB$ddUQ&z}WOne&jN3#S=MU`wD?UK>_$etIme6Fz@KraLt=q+rnrfZ|DU zrmXb$va?Z5+wH+x4o7K!KgxxFJHE5@)Q$FM1sqj?!;O3eP&&bxPz6z-9_aQ8VQYnO zQ(5KaXw**QLm z&oXmOv)&GIrJT{(z>jgXIP`T6eT(Bs(wv?gzVn8*Lv1(Qb~^Gh z(%@OHdxCXtgmtbyCvTcIR$A#1HdR+m;8}tmaPiOoY-dBuSgwS{aWv+oOYcRAjy}IW z-<-eNYq$**;p6R(dn(M)6=s8Dkz;WPPLnb0-t9DGcV{_TYL1?aaJRHwjqjFv~qbjIi85_yPpngji=}{L{darr! zYTg{pn+?H=q57n{+6ON!RB_|FI)#SqH}}Q_%UfpQaxJnF3sYuMkusZl!9Mqs?6E+G zo@*(ufkM#}2iW-Cyyvm_AGrJN#nS@CdA!9~ekH`~DMB3rBsQu88Sak#sL<6Gvg{P! z+Tft87Z@HSi*AKwYMCXg#4^S2Akn-Ixvww6XMxo$)?1wCHO%lA&)>1Xw`HiF4u}JZ zkoe%ld~!qklo1oal4&p7T)gQS4 zTWaR!h`M$(1WY=4d}c#q{FC{sIc{~=1rh2X?o=*0z6u&!=IFZiDs#Y$cqa_cp<`~X z@GRYkJ&R%&>}!$k5xj?ki{FOfa~IP#I(0x;L>}{dn20L?dIqz zL%_oM%`h&twUu!hG#qD28f$GJ?=BY78;B9J{f1kIVreH!*^Mm3DUR%#nyGD#F)7yh zHJNkU_yk(}EbUC7%HN_N-5v(Eg3B<|YcN%Y5v)AxV9dO2U)T+|RveZ%4b$yCwB2&+ zuGlRuL*@*b_K#Ou8At%f`#nVm*oIlOcEx4G4T$d1Mo=uC5Wl7Jr3+k2-RdgC6%Wek zv)uA+JOj-tOks}mS~6Ug$s4gP*-x7EmVjY0bkTLE3Ix789w0SfnT!v>PtqPapJkYA zM~-auzhrnTU7Cuc7hHyHui+LSOL1Z4DjJ<@%W@eqdb_(H?A_m_YKm)G(zKwt01N;p z%hb#yFupNvvtwON=Gr!*D?sYFb{o@mN+3-k2X;&~Y(rC=`L!wf2*TvASDcAs-rxR- zp%Ya^hk0J)9a3Dw=j@y4?Tht7&!ultEPD2*X58VD9Y17QEq9Ce15wN&zCz08X$)lmWG}UkwvjZ$! zFl=}SeI%r)d)pArRA-AAG&01-8gNFS57%HNoZX=-0N%puaq(Nf2fIDJZJ%dfU3Ga% z-OOh40gmo!PN@TycZN*45#SdN8`!?M@5cQtI4R^{t8l-*E>lQAdB8xS=pqU-oYYu*neS@9D2eALkGuS!nB=*#PFxY85 zIM^8j_XO6SFaHKzID~;8Q~%)w)5D?lCxoFJA?6cMy`T?B$_~kwcIg`~Nv8ZA8!=b! zz0rRa+ZUMu`t+y5*{vt)H~xj99#RBkQT-Fk{{zeG0ZDly&Q#P}`h_lWBP@!Ll(GmS zMk4`ZV5$Ggkb>Zc+hY3rjRR0cG`&fcW{3J5s(Y=(*JJCf)v5!9Iy~scr>{=TG<@ zSP!fAJ3b8asj{4^%#$rxNorc!oF@hEf=j$A%dg7x(dx_7H=Q3mUo&#P&HoRwS`8e5xpK8H2ZHr-FFM}!)y(B{88ln~*(BgF2=u@@@<(;$cUc_Q2Y zES&zc;6-&+GrVRE8WCj}-0yIyo^Loz`D9Tbo=OjdnjR4HZl&Vy;#lZ53meC4Z@?zvBX@YPKL|!_yQ( zGQfSK#XORiKM(?d=$)5bT0#w79mRJ(S&Cnls=Yzq%p`UB--6}f2e5dU_Pa5{Oq~l3VynMgh0mGit-eyxxF+_fdUvZgn5rUz~3B8^($=u z1NO$nwI>VZLm@8qdoTN)-?17*lY773)fYIkzuaRK+V+VK78LrUy! z>pB5W`o;ml6}2Yd_)mY|SK7Zt{;bpT4&J)it-Ty+_-0gk3}Y3=>UtGX2|o-QB)uL^ zr2N>1IABREw52%?O3v?4h7+Uj|H$MWLR9HN0dr*O`+Cpu(S{> zQQ8uGbdyIe%s($`V?!`RTyr*F#iyoM!07LKgRBXp7?mFcKh4`gB1$|hqm}OlL$LP> z-@i&~f&n|!Kz1Nk-a*)u_!=&VBD^a!Dmn1Xk|k%NXjFNXa6`Rqcu|{4k7;2 zNmHYySgwPem}ztvy_vZTb)RMy77pp!vP9mobJC2IDHjF2wjbP3V|UmU&kq_SvW@rE z9dF8Yq;4Y`B2wx;ol@NA4sHDVc3u(}DvGjZ3Ge2^CIj~CUxNDv@{2Fw2H~E-{{6zZ z41sY_5kCo2qON9sON^12FA3U2>ZpXWTohJc6xP0fi%nXq-Mkzpj7(a=D&z&W=__Ok zIrA9Jcgvw9ux7hbAy>pGk`*)fZuQ#Rjz)j2vV1G7{#K~`R@iz*s6Qh-CXMb>SxyV9 zPYac&@pODV^=iM08l6lUTl|4mZ@(_M(yScc4-Mt|+8-LUH#dBAEJzntO(pFmr!>&B z8-XrTBJPw7G#0tzhwn`<(U7M&p-&A(p0m&ZB$AO~6_*XoAl#qkgilofoQ{_xatSX4WVd`x~M`83phnC$OS5?4&qKU$P|h8V#K!g63I@@cTg0j}Rpl%KLXZq(rJ#$D`-dXS&(rXRRMEZL^p zxcY=2+OvP)((zP&D_3v$p&bf~D+`Gp`Rjz-- zYpq#xU}k~LFpMCm!*CE#cQ{&s=8hSWR4Rqiw8JBef~RaF8CJjA&5VVEQX8g&GiG}a zvqJ~A?qIA054&Mf1P_ID04jGlg`t(=rtFceF5)c~tut&qUwjuDq?A zAc`=Z>XlpnK$|^U*M~0PXK$L#R7|JNC6Tj~;#qT_y8Q294v|xyH$AVRexli)P+p(t z5ak_k%D3%kk7}8C;y}l^_C)8zj&#TLmWj@p`gbGU$0O?}N;FivvWM=NSi09Y>wxRP zzP((dAIC0ZhRxO3rG{U3+FUHn=?U{sRzS8b8=8ubjA7wyBwEJI;4Z+{;Ni zcuv*}iSJD_ncPqLQ=+^JdEMpa=r~*6#b&NW4z>cy(2X0q4{-0$`03CPcpD@Zximh{bWmZdbJ$yq9K7Z*x)UB} zlq04?=S*~@*6WXn>%G4B_W9fiyT;v~anR)ps>|O9a|i`- z#^oIp3X%q8048e=|S#^}A8Pa!B_q&n(SAd`agTY~B*5Vk) z94DDPLTQk2o=`YhageH-DM0BWP~vEy{|wiE`8UFqjwLCE{*b>ekrc1)_hQR;-)<2s zl_T?Q_Szr$dX;ljw}*sAShrv56-D-cf!#PMr7LwaDd;bLiA?gDU;IWYIT}w^{IBsI zUO0u%!T*=={|NpI=@e0+LtpC8 zgyquUrqgyqm|sL04RXIh=8a@(S zSR<2gHoOHg$kzoKWP!&Re!$`LnC)qlwVjM~tBW^p_Gy-eK^&PChxneFHIi+$kg-nw z2#2kBywr8$B?B)|@df}g&4mwau+s@oT~l1_+qv0pNbrl9%T+#Jz7?&PO& zi^1@K9Q?6{1ndwgo7|S^Q5>dGM7Z0Oaf=Z>d)}kCtlOR3dP9wvG{5kN{^eDPU}fCH z%sjZidD2d~jeR8Eo&Z~llsApusc290F#Y@3 zX1ZAsP>VfGeVl`pUepdP+j+Z3eVF#hPqg-pq1MZnYz>3IgXQ8H|DpBXD~6Zaw&p~u zM~>2Z)#9h+jr-UoC#3B)rK?`dTYN0Kc%JV5oOx_}fwk6*n07X-=id7sVLkU+*NS(= z#^T;p2Ium6ZtH6ehfX;28o=~&Di92pLCrbmCNdQt@E%k78{>Ku?ec90Pd=R$-y_eO z+6HSX4^>XF-BlT%*MJp>Y>mkv6{zcF!#o?fFanl+h7U1*J1pKLarHs7Hx4rLlAV*= zi`uzH*G6ZpfF~lSJw=Y%9!p+>y(7Nc`9TFv{^ai#3#~2FLg>rynt@g{73qv3%$yDADo6b}v0O ztZCaf{X?v_!3s^71}i4#z6NF8TGk|@-79GCBvD~Wb>rKO-pOKPYqnPr(^-Q1qY1m> z5;-P&8?INzL#cxplSmggxIOZEwr&=Vs^jx?c{%%E`ob<^3W^fSijr^z)%Gw|lo;g1 zWeM>dTb)*2s%y+u3ss^l#^Eg!$KVtL4q-x9f(|OKb!k*po#zfNcA4B=3=7koR~qIY zE3P`R^gf@TUGiXbS0de2%mzf%g)akhpy^R|EJl09r+G(Bm~SyQ++wY?$?UC5?M1QZ z2El~b;6%piDt5oWQ*$+9%fzSbu9EE&$Gcstc27+5x;8hyO_LBIF=$; zzW0MA$z^i$aRxNx+YJRl!#zR6;-Fzk&~TsCaIe1zeCtKt{9BNuw{^8YLD!!K#xlw| z@w}l6Jf6Lz+n|v4o0#BDbT#kOO#Ief#QZ|+eielR+fpl)ZEoJ347m@gZUO5qW+fiJ zEFlT3>h@cqeR;6jcd*g%fIDHr8Q7KOc8!a{s~1>Yvhp3_?TQ_qyn~L1Iy+AfX5bBl*J|4t$49A)-k~SyPC*8%j$SRBTa`Z)KmB*F!7Zdq3MUt|z5sruTSc z)hQ?q0n2B-yy|{}_32$po&JXRtHhxdo@lNYh|h>mvBl9sUeKDliBLc|T$4P>-iX%N zNO+K>eURLcImtZS!HL6d^vEeKK z8|z$(>8!kgb1rqZ+gC@~SL4FJ^adRsXVx>CRjhR^Hq>bnjf_o+$$m&$?vZ8U!g5uV zBu*zRW{P=txMNBST;=95p6Gfpg2(Cd7AqXv-AwLP>;x3e-uuuGNV1@xqTNrvLi9s_ zP#u!n7$|?r4-;s)*>djX`^F_XRF2r4y(ya|_eF`wHF?utdACXoyf1fwn0yNj4D=_q zE}DWW1nF0*c4p@|=x8%1k-Cr?Cv1yG`+|^RsLkw&T$zhCxkT=cfhAAMJ&8?se_dD4 zIv%w$VcGkw)jO=GWY93)6*JN7Qrim38$xsOi~x&7hZM5g$5%Fl%Rd^vt&cvqsd z%yq5n6yJD;pi`8F;3SOU)sn7VHT9j}rWIX8)uyDI0=+_;wl7f`V|T@jo!Q`#LxRy$aQ^|wFyu{-%h{iF0fFO6^he`oZrz{q0pz)M#cT8~ z%0wilXEr2vLg`7iZF*eE{*;R&^(HiKh3u0)pEc%-P5F{3@!YyR3s>LeT6K1a59H&5 zfVSt4t8De=)$3brMyt-qyM{QX#2pZbJB--s5i%za`DBEW?t9>0l=y0f= zGb?^X87&WxK%;FH>h+KhHx?-iiXu%#5n8(zhSUy0n>hN=97un=C~O{*Z(=vRaG~gh z3&tp>SyYgqgZ5E^Hkg2m%cww+y7y8+k*ei%{+0^|aw??Q!a~#+DW_~@ImebA633$N* z_4bZt@L`dh=PYmp;15K%1hwt#NC1Gn?O@sxgshy~cdTi?)Og_0mP>78aZ?w$=g!ga z)`Z9#5q{jUm=p}5B_pA|@5Z;BmcW#nKS8!r*B5t0Y43|lx6HV(B`P#7Az{pIEp$eb%LHSnG29GQ zi1{n@1wAkB&O(A!q4kdVBfXTUxRgZ6$b=apcb`HcDjG-V_hBBLTtiOaexvr(` zi;I8uj*(mufXgbhMOH(5H53ZvKq*Fx5bA?Cck7dOCZ8|PT@tES2Au+s`?^jBoi(v6qbt#1x2|tGSu*&9!f0!*^qxcjs~O`!e6^copN(( z)y<(PD}_rk-a}~@C#6JBSW@@13Gce3J=PReMv6}JR1gbAF5I|W^ya}5C0=u(cF7c0 zRQqg7E76YSn9f&}_RyPem(oq%P|hNZ&BJu^Fh9Zr&?%`5U27Ufi_lq74xAgAET|5j8*190yEG{`1_|-77h4^w+Wk6XDu`xzZ zasrLWk(!bC-{p$0@H(&oRxoMcq9SK*(G-3V=lE3ok_JFILo0nN)L5_bXRiXslboaI zN@Zn2@zER81;W{NIKqiFEsQmd+Ou@$rx5f+HmB&u!<{&|_Yd+zOe#GvCjH|1iKL*R z!v0b!p5yjK6TfdpAV_*rvuc zY&pQR4Ue4PtH#xRCUUEoKIc&1`(fq?TVRW&CRx4G9CZl1W)oo&S5Sb|dyPF7Oz1{`JB~sOpH5aP70Y;MAUbrVyKt4ODpipN0h|sKKW^6 z8irq2qH5{BKsXf)XHDU>HCzmJd1$VA!=>(!=GsA}>!7w;s0TP!=7ZY8(f7fFMCa=G z5ueQfjkP-nh@9=&VVEcaiWe9r$9w#q(d>7t&t?o974K@dd{$ zLjV7Q6uJ;H5H84bplog(gT1*)CvpMyuEbopa)oBGxbSUY=_~9yrZC61*6Gw~z7!ST zsn~#xOdgb=<|~>mkqZcqv>sgT&%`s|hR#cqsGV7GvO^~{I?nn1a|y#g`;56)a3ao! zMW-amZ-@w@GX9XaH<7j`Ql>;v=k*? zWfuSe1bD}P(;}O?*+6MS_gHYS1#XZalJqx-atBEcq3%Z!>FbgF(7vZb?K?Z+&QQnO zs&nY~)O%t}(o@=XUVmhdsw7^qS8<9xEP{w>1&#R_`Qp-yp_=ul*koLRqb35jBB?_W zRi>s3T-EoD%`oa@JKw~4%%rBKvYRxLd6%nR$9Zf%jotpn3K5k?bGoXHHhk*mWs*~d z-neX}h=dlHZ!JFu(c717CGW{|uWCY&wD{;>eii$VYGT)eApWRGzBnw^`FZGj5JYG< z6XiD0b|%`(5CKS{Ll2Y+MrAcn#a9W6@d7ua^fHkc1z>^hJU~ub3N9X?GQhcE!cNhU zo)zjvFcaY9q9R+h$wAbbgyZVS#kA1bZx;H@f2=Pkzux8eg>jDWQNFpO?nTkR@Xbfs z!pf{PLPFOSv)z%eE^EGYV4le8U(HF%{H0GA*5p@CvqmvRqJa&ffd!dDp2OnZ_?eQZb2Z(MmI=hhiLsrjUmKq~VZi#6+1{u8|BHf7g|pEYM9 z11GL)CaBzMiT4N_WaERzRdv@G4)5k)t}vOoj_UhR>z&(b}^1G`O^ny-$4ZM@9ec<&isloV3^h%HUl#y{lQ1ib)m92e)kR|210WQ!@!f3L)EERi$=dxD$Vb+4q$SkeZO zi__bAFRS#c6(C%r7vOg;{}eJ))_q0=*~r<0uoyCUFZiedR*CNr99!SH83BVF5!?n@ zms-(fQ21G;@2Ei&Pp8C3X!b}_rmPZ5KB2x zS1s`!bh#CrRWZh|i0~@Hk*ZI~0cqpiWy8eJ?~Wn-H{7Trle#02^%mQdHA_iO<{`si zkD>aa%X&8#ygR(>?g$~9F_QO$X^@Ql(_4o({P8QxlCNs-`)akw9>p{*`3l$Jg-&z1 zdA?pl9gCW|DJo-2RO0Ta=>0DO`(WkyloKV}Z<$O+rO=aus*K@WQ(L@5Oc`}!=ffA$ zh`kU=XoCR?{A_>w1Kby)M^Y5gJOQtJS)1o-y{YlF%1K z>ddUD5dR8UK>fQ7lbXj{4M`?Lisn_7&0>`mpUafL0^s<`DN6}2+1-9!vmhFr^2r&! zuHFsqaFIWpLZe{PLA&$`dUu_p+IIvL@B>07&D!ulI;**uIFjPxS5n0eOOM)?Zx36_ zjj_!mYLDs3ik_=m_zm% z)tlc&Y!dT8{A_V91T(!T+}}7o*mDU|w?gDskYWj213`CLF`~N+2qa99-UM;oIoh2d z>q?MhG#OxW;Vug7vE=uJHHG7PNaaE`L$w0vbfk*lLyssJ_6O)C4Y4CrrvE0|KZRa$ z@*A&<`EL*gYFCHlnsL|KXAUgzwHm%U^e0@FYEEdDPPI+!iP3~#Sen|l3r%Rt&&;&V z%Q$QJD)VKB#U8c_(9??Q!<#8Zw%JwVTD(H@(G@Q5I;~j)spBOJ2ID!0dcb0XhG-yS zr7gx0`xPM64nipE=IaVizP;mGkHsSgRlCAhH~iIg<$KMyGa{9G#i>4Md9$LvvuwbS z(qrigU!&POU8~b8&h$b08y*38Oseg_8lY3qRa_;JeI?pLHcG3dYG3&&Ku-!e4*s2> zMTgv%|GvXwY=TYOKTrBF#(%`&S1Zq{tIs{_I{GYi^z!*5mphKUWBE?7A^Fa8q2G$X z;08vrh9~>~x0ikXj-h#qdZX9_uul;|fdkis^mR z1&2Mr(`2!PGl*R2?UB4&HSm1o)mH%T%|0$;V$CK=%kQ)->=={5I#BN z-uWx{$7`3*7-^%I79NT5e~31UJg2TXhXz73ec5s3%kxKO#Q2loZYK?=jv`){FC4ji z{Fk;HZNt&u(YA~yI{-}z55H)zS zvV!6@^JTNc2ESFdlMKt9Y5$TBxB9}qywSApq3qn63$t#dJX_p0_%r0oB7sL3{YaOlzDCIFk+T@*ww2Qf@pQ70>E z#J?uCvy<)nf*lO9LBsTbLEmMV<~3w!)+$c#xo*#w;f|;CGgohoJA8~koC}0U2y!*gPDa(x5B9Et8%rh{tZXSR%~ihE={FmWr8W;O$c)fD{UwvKh|4vbmNdI zUbKEPKQ~*FpP-v-flZ{D1@R+n+U(L*0~XAHW7_YozP@?j_tco|p(fvZK4AY|x$U#T zYotI6uXA?>TO&1TV@hIQc8KC7%@#+PRl2fyvLklOElkA`N>e{`D%WEnlVNyJTH5*J z_ZQA|VlcmvMP|Iue>LMx3rII;UdSY498%FrPZUUgrjU;uT&vux09wakZjg;S#bBDWH3g47{-;cgH!tQy$IZ`=Ig;haXgczQpA{c{ zcF|FG;k#_~QFZ&fA5Qdtx!kWVI;Ym2d*}6`&gfqp8FLw_v1c6WoO>^&S(ZAL(>$5V%@Hb2rm}yj`|YV@z8bCjmzeE^ zx=)xyW~7HGR>Sn5^ij>{Nu`_gisgL(uYmIwIO{D- zhlBLALVT7)4vsDj7Wvg6oiXD0C)gMmSu!jiTo2OIL+*XQcK;fQ=tJ(EzjFUKnyz_% zOp-v;q<^F7!gpuPB(w&hXFTZ#t&;>=&$vNr?YZ~g3=tS)fB&t}>%9~GIcSH(b86EK zx6xqGXw<~2HTSC_nmeJr$>%p{rYPJ_s`~kAcYc^K-^fjN%9O9rK2oiJh0^D9I%%o- z`RYMkwa!lI9-!RP4TXhpU5sp@I+4Z9fCE;x&57lii$XFJ18<|JoQbv3{)suth}fUNWZwyD1{K1mdS{ zYk5wb`v*+1uhDH$WJZmLSA|xHgKKTfs5l>k&x(`#89)l+5Zu2APdydP`;yfq{uuKwyAIa`3a9mmL#eBf8k|-%2liB)xp3 z$KedUsNKND`E$YrbjAqbIc5}g1FT}&(n%8UqN%F!mAbF^O4lV(1AkOf%}>psnr~K7skOIII-QPDDwTAh;%&UoVQOpd)*s8)Kkq|MKP88DC!d^OvV5e5srJ04Fic zU8Zx1Qlci{B1boOY0CVG-Rl4Gl{&=79==((m>vf3GPuF>xp~&POC?wMrhoFSpYh%5 zoUdN$9lp7vxaF~$PkHX@>CgDHpYoPozVf;Ai+f*oFHU#PZGzl=ab!&|4`@SovC>|w z;(o#rxSk|iFC>fij=3$1Pk+px{gA)(0S}$z4e#?H4flk7ae8I(6VJ_mZ209sab#Dq zvgtjfrsDqV6maMsu$&~+Tl3bMqia4|^Mh$IWtw|`AUYihbK{zCzBAsxIIShxJr{hN zzJFHt{j3ZwPQjwkwK%o&qB5{}l0bIQBg0s2Qm3STvGjvNlNA6?%f2_NQ#e_i{XR9A zzLnn#OgMyV_ImsrYbyX|Pkk2v6xxlu53#dZD0%mk4cTqh?gL>1CN66CKNuXZt{j-R zyLm2`sNLuCPL3=N@bv-h2i1Y`DeYZ=O*kwErd&s`YkbO(-)^Uly(XO>t)B=r?94Jw z&55d>NQpJUUPVfll(UBT_#WEfZujnvUD>5dt9N_o=7LVZmiT?7Hv12l28_G^@z6t} zHthF2^bpR-KFwbs*Ok_;kL#^>+W>yJ%veBo1UgiDYn zTy{Qi9*V?7OpEq1_j>~MDzHF}_Y2E@d)a{f*nzMf({89qMdO_J)qzDr*4^*qamm`Z z2$}ldCuI8Qe@>>bERe~2oc5X`eod&4v#2=Giwgtc5hATw-x9DF&?#x-TS#etDUVHG z8L0fFL@g?m(P&&Y`9=}Rpcnqo-c^Bl%rx#im|ARFlKx&)YUCmWey_2Qt8JpC#>b`B zR4V1I8LKB{j!lj6q~eUL^Ivl{85&Dg6dDYgjpFi`#A>DiN8BDj>Pexvg++$KwIuGIHy>h1WD$(n?{6lNcPR^D)g-kZVl1Rd zNn$W)Fp?O)&nXM9`i4`_3;w^<<51wxyEZUlH{^d|uluarJl1TgHfpH!r6vu%NzJ*V zn7p?S=yrS6xkJ1^nl8Ch;*7?94MhY`LtSU|b{1)l(&gB|f6|3yIqL8G-}txuhCkxk z)DDqN<_KHQKAxHyJ1@qQ26F0;2o&A4qSRuls-xF z+Igh>C7IB8ZzkPHwv;4WpZdgTvZZK(v<~6>H_Go$WB#oInj_*;ZxSSoTyqTNuS$jF z_vh)L!~fDW)Jwhj%^A$Dq|I;M%j}x5`OS??L(b+mlbBuiZhkX|*|l=>n=6@yjhpu; zkuHU3J*2M3l%?oYBZa{CSIScKgS7KPp+a>%)OBc|&<8pLHTwhq-Uon;KGFxG&<7}? z6R^(ciedh@x&UeB8}0nhul};Si6cy> z3hRL$I1U{{<1?pn3xe-o%LuIKGZ{4dBX(9~#vW$NU*XiZx4tnWw6n{K)y0SDr@}@u z1A`|SfZ;b9Cgzzf#zuPI6O%dofBB!p=T-V2i-FgcnNv8@EeOi$2`!J9W zB+{0Fdga^07T)LwgpMy;M!)}W{iemDexs!Ob@fn@)G4=ml^I<=m^5SpdCJ}?S!r3Q zXIw)$Opz<5oDH>R#jM+iW?Fw#rVR|S}M*QbK#69x;+u+ zWBc)IzSS!V%M6-r?9NSGUitP2Thu5EuQE#A@h)PwCvLfogNQ_xuQG8>@*2P?x|4}u zkFVVaY24yVbTck{$Z9m=yXCguabaBEjvY9S|7U`@elRLMBnG;|NQ_Wo{vxXG8yZsC zp6H72Mma2)JFF_MAc^+sr2a(8QBeKk_nF*Y#ZqyzVu#Iq<@@9=+Ea#&Hlu{W!t~WM zA5XNucWv-vUuXAX}sy*y=)Ck~_!Em%FXbBphjKUXH&b8mtuMm?=zoWKO#=kN)v&%~k&Y<2>qn9k}GsXLuYo zLKqome!MoM`zOq=76N~z)6vOgVKvKwuB}>k8iiY2ll$>P6U3nzyc;E@<`-CvOzyHh zrtyRoxM(=qCDnUk1*pL(TzJg@|1Ipo!Zwu>Sc7!(H^T@~P=X6hO7oIr!x#LtvzVVL zRW|ij?S6I=JPUqM@i^L-s|b>kI*bDm|m^O{jbI8uVc2L_nD+PFG@mO;~aTB7%lgRCazV @L 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

    59bg&95W_Ms#~j8} zv?Tcqr3`F-dVuk^v<& zKg8)|?g#1TWTy`TTi#D|I@#L%P^Xj4ex%c@z`PfT`MS^;$MeB9p5G5X1R7)hH~1#J z-i^N-9!T2ZGH^#|jPpIPwUcORl1H+|kn41^)kPaH%ZL8kJNpjKo@{=)IK3;F>j%cr z3p_FKubj;yaGRh%?{u>H{DafifNlQ$6WGT8H^4K3pEDjz@<=v6qnus^=6;H?O?LVe zr(fW7vds&ZJDqI)uXZ}QNAUlI(^r74e%FJoPE)_rJr3)%0~jyT4$0=fi_^=%4TArJ zoL&yLb;40jC)>K>WTy`TTRDtyI@$cxfEg#t>QZMzwz>N@r!NARz?N-z6s z1!eUq*w)5f9!btE1KYVhoKChfIRtEd{6xp2!M5hP0h|gk+z7UD?qkH_Epl$2mMZ`ldbRH@AReM{e%5S;J$(P@0WaDIS_2?=L^8LAG`z%bBU8|`MnBk zWA$8c_u%tM@Zi9uhb8wU?+-RVWE8jLcPQBW3t`vaQWVI=u>P>5g+c+1lzvr%wWR2>x$$I@$K)_c;At zF#XKL-vdr3oBi`nCtG`d+v#M}KXE#lZG-;vE0X+@xu>AMi_^&*H>j67oos%3IGxNf zg!Tu6txo%Z&Hr)Eo^1K5ayr@c^PD~dY9>GwthfVgJ|6{J8-Ci^ zldZl!ayr@S{oi2vWLle?O>{(JlLt1NdSIAJ`<#a{rvx0M;V%Pou4Gxg3O*gL^x129 zGB)-GbDu(eAhHJEw463nrb_D_OYp5)(yZC&~v*wzWD zCz3g<2^gm0zc{CC)AAUf_u+3{ZnCc|LOC-w$Wa-X<;HX?z_wmJ4s7G|P_T^^)nHp! zOarq#8QTmnvLWTa0nfh61vA}^j-#>=!x4BBY&IFk#mpCq0EVCIGX2b#W@vgw0N$K*h^cq&bYJ=yGsn2!9Co8bidpV2i*mu!A! zg4vd6Gsp3bj_&|-%|n}IV4!@SOEy1`IQvzOUvvDP<4+uCx+VTgz~-OK@rvn|f!Q|r zIdVDJ((MDb*am>@Tr%4*&mCks%Mi@=FP}%F-R6MV7g~V1PiLFlFX(Jzy1ZIymbbT(w#@Z3|JUKw=8Fa&HdoEvn;aGtZD0k#+}a{8=bPd{_O$KX81 z@LJI6=M861Ha~xH`ubo`KktD#&(cr56&gSNGyvlz$^^E2HFkOvFyE)27GRt2%A5_^ z_LciNy(gG;&2#&M(ZnT&$0~Rz55bam^r?+twk4KBv^?Pq7)q1_8?xCHJ1#LB^lLKb z1^Ov>I@!|g3$}DCoZjDb_!;2zfu1v5_84VigoKYa$w@}a&8 zT!L4g`yu!wF#E-kM<(Bs8UnU?c`O)j%eiFRGoJ1AbHFLsFs-TJPJwT8Hg|w|F4J8C zo(N{Tdmokf-;kG#fl+=K11oS%9yXF+*q4FnpXZ+D&K&}_Ya=tjw+5R%`X~O$Ht+X# zI+^|%f4S4iR?dCFHrH1=8?xoA8ayzhyA0ee_^AA+VMC5jxjk%u8Em(=IUEWGR?!gEj{_n{sE<7i`y@PXTl6VqOM2`ypV9 zr^wzhnF_XiT?jrp*gWTK$QVYY{OcX(+7g&$#dw;4ZOzaXJRLgQ-XdpDwsyV@ zY-{`HoDJFPr*EOd*){l zn0cYi6fnz&e2%jrn?Bv?GXgVRvenTna69;540nLp4(W4QuxGkt^SRt~_#vC0m0(EX z=R>ev4=gw#DHF2!sqb{Mt-G3-4rgSWBMvbgY4id2#Vg~y7|a;SSAf~x$hU&ouEU6T{t(;Cay^Yh`f~~B2g4={RPX$|_ z9p&t+0<#{g!H2_sCjNc{wwNCTmj^$efvsINKQZy&3(S16?8?D5pPmG65Y8PAw)Qp? zJS^yMfq`-^*~&2Qq(mok{$gzVIGsEe8tdgor{4s&b@fwDC)+igmz_>#{PeRH+%0g! zlaqO;5g4+>*(5N_tp&IxbjG%SVEQ=;+!(LqDzK${7TD&9X<)0z^TAez7lN(7Ujl{; zNq2T&rc1Up-u2+tA+7tt=JR*Kp7k;n`wPy|jPq8otrPA8pBT<<@Z`G3CIy}eW}F<~ zW`V6vZv;bdHDKn8e*WmZcnDxst`M}we?ep~drzGW842G%rBo~00R&%G5tvp*gotz5xt({&1 zwz}KX>7{`gb4RfCqZ6GC+44TZ>11oSW1LR5@nn+e@K3hu2^X1;Sjntk=5dbG$>x8q z)8_?d%=5v#*2i*w11^hJZLI#e(-#J2UdVRtL(XOy*!-^m+Zy#XXY&TQ9rDdG%=$bh52yraOHG*z$6z z(-&eqD1$$;wfXT*uL;bU$rjHfFvmT{f4j3M+xT$5)8~Zpp`UBP$A)wN z?8=R7ZE3yJ-vu*9=J6k3YlojWJ#||0J-hwDFqM4u1n&{fJsoWIRpo5R?Sjp0u+7)k zJDVH8?Ssw3V2fb|*nB<@wz7Q*40DNrZ2BusC!7A7)5)g4;q*5HvkcdRZS9eJdXg5o zafq|2)0=^99oXLKWV7GT={*B8Ei%Un*7ae|rUGo|p6Yb6r8UawRe|Y~%<-8%&viCr z^Le?`$>#Gqr{55mKIenY=Wm=1+2+lqPA8lGnA6FoKkM{WfteQB_FU_n4LO56v8}Fm zI@$CMro#`}{6vG3dL)~D#_43!i%o|gvaO4D16w~TF&o4~ZWChb<8-pM;r>o1TREKQ zbh6dO8BQmgpK7O*O`qcQ+Q2MNa@`!^Y%T%YTrk(^^8)i+vaLB6fi3U%ID0bpFKlnm zIei(%s1j)O^CozoV6*en$+&tv7;==sADL@o`WXbaG4luD!=SO=ZvwZ&>(%&s42%~! zmuz$TTBnoE=Nn-2`4+eX&ZWD_}pef9?1=U*o~n?b>b z=Tt>KVl{3!-oU6g>^ z!Iovx*KFXY0^Bv|=a`OTvX#|zr_TUeY!^9wR$#_98@yle`6!ruf&J)lXHPc&PdR-h z*!({SW?yF6riLc-bpaSsl)>LDToYzF=j?^?6S|ZMVrT-kvTft+$>x7grDRptN>fz90_I&Y#U>oeRa@jUklz7?=yyBa(cPZ4N4TI@#)_pVKP>(Y*9+!dHW|;0OXG5-l#`v3!Ov;mN<<`>aWVT`2@9XrQV8+I@dV_6TJ<{2b&F8UB z9}t-5l9?CAS!p)Vhk$KgJjHa_)CT6cGr+d4yUW>-E!}&az68wn$#YkN+l6#DfUS?D zMkVLwgJBY7_y@MN&2CO_3GRjWSw4fDPPR47D5qC}tuNO)oov@I=Yrb?pG&~0!1sgc zlVjtPV7y3dWX8xCo^v{xd8Gak*!=tp%r-_p4XYAA#b92Or+qIl>z6hMJDYN_?GcW4 zI@$D7olZ7=l+&xg$A`4;2eS_{-3J{n16v<_5{$Pszu>{azI1fLKLVT0tiY`6+2G!I zVt<5~=bh2HyT^r-38pEiJOmWzC&Vw(@E1^b)Ye*52vlwjr$waQ9ftZR)rr zkLQ6c-I-vk$4kNE!nqrr4f)`p?>9aoh1I~?HnWM?x7Y15N- za5~xaQBJRN`dFuvEzZeKC!3#hoK80VJg3h9TV8(T^jU%9Qp~5|ZYZmn_hrZL`hK19uO+5^U{%m9u{i9EZAo{)b6jzX-;u;*)G+$e*0P z4%|5S|G?>F`z&>*3HWR`_&;@G;{P}M)G=(A{Z!oIXD&czF@W!-pAPA@j%Cwj)yz0c09@P zRItUc9&y_KE^kUwJ{fR1(q;KnfN75qqW<8Hcx8Fkf~{VrfgwrXA=~^l%jsk*pR1it z?t^m|;BU3l$>!%zPG1K;JJ_FcR+4UIV8%>le`K7~oDJFf-33m+Ffh+0+jah%oDG@t zJAE#8I=N@i-vhHPF)tgz);>2m`>0mSfqv?N@gn6$&VYGtW2ZL>%-G0Q7tO$Xz?Oav z3Odj26YS}WOnd5wIh~B=DEfG(*MRp9`t?pH+g@X_)5*5xdBW){!0aQ8?PaHvZN0bi z*-2UL4z{u?1zWr71cnPKA9B;+v#-<1w%16AtW1POexRa#C*!~-A{+FR&upZ^! z<9(Ct8p6ZEf~AxH0@O?`wj6Ca|^D^f`&m&S1P9 z(S~e$y7o>do8H6eJp(hI-e4Q+s=!=hForXoJ=w;nDNe5iTYjfGooxN_e5aGm{$i&u zK{<1e#`1j3l{wk$pK|)jz>J@4^}Y&h{qc2YPc}bqJDtojXM5OaI((Abhj?24K50K> z^V7!Z?E*6%vR&uw=4`q&}L3=f3Uq^?`XV+4@D3b9K(T z7>k1zVC%nKz^pr#&mmwdAF}nkPcP5g8MLy9u^Bb%QCon9W8@sKUQ)nFS3#yk5Ou#E%fI-P9gIm78>8wW0QI@#ud&q?c5WcPG`c_{YkL-e>T{&PFI0hK8*QQu#E}t zIvcW`yTR#X#=~ig zB{A#{h9u{<2X_iK$Af11ms9h}}VFk>V4#kq{F2F!A1 zT9d&xZd?@XSw3X$rx^duPA8lGFqmaRKQB5PvX#|4PJb^j<00Fez5zT4J{ViY`MRf| z&*Q=DFN|Rv7%x(8WX4E+iqmVswkMkI^ci4ld$Yhc1}+9$`~R)8Ujnu<@KL9et$nU= zI+^|%&q}A0&Hg#3ufrHv3tNtXL(sph9gcNJ6b zaxh*bh82P7e1p^ zYWSn1E*gVjBK1h-=bOx9snf|P27Q{-$+jlB!s%qY-ZjtZWb2Q=b^4OP%;VBv!!rM) zvmx`E4`YjGB{7pt-_z-(U@OlaPAA*gyT8-P7W1J_?+a$xGPZ$WYY&%!tvz4i?B{^3 zzyH|j^T5{57dibdaGQ{?7r^H8C1??}70mW%53F@1QrIo!FOxt=}CE zHv0;&)q8)ZlP%`KP9Flc`Eiod$>yil>16YBnbT*3ix33MZ9bUeHp}goVBW*Vd_4}f zdi)FwN%A;jzvLMv8!%V)hE6}-U%>CvrPR+^FDCPBE!g_+g15NF zIGt?me<|2}KJRSEcJAv=C!4;(>CxqGo&`UQe6dVk2JeekmPx}al5!{pbG<~psneT* z+XwwXrV!D07mX{Np%|Ni#?{Kh`73{0Q zmhJ*)e=~Sg@L6w8GHy2oGcCrw8@O+<84L!BJ_Ky}stU|})qt&!Pj>cX8}}}C`fM=! zFWb^W@I)~E>~>YsPy2wm*We5ZL_x0F1Y#OvtHVe;U}zWG0yXm*x38XHT|#Jq@;et#dZ7 zgRvcw80uY{q+1^hS<)plAB=NnF#8LSTYy>qG3vSuZ2P-Pu(h|LV4IuH2HQ9`8*F=l zpM(3u5B>ig%sGO2={;BPaUu@^o6lij+Y6r$w()HVnEi!*mV@ykagsSUQvbcvSAi!5 zz3-0`dved9PX$}LGr-oCu5k8?FqY1NJwKD>SY&&R%u8X+#CuJ^>~}NqHwfGquZ(95 zI2E`WZ1chlaAnZff`O8kRj;~d=Dg+jZ;n55{F&q2H^dL)9FUjr9OR|0eS%ppjPov+ zN3!+Ddz?P&S@c9pC^C8&Q=b$YwLn=`~>X z8^&Mjbh6FY*EpSQbJKNBC)<6AFFKu^8~nfG^tFLm7wf<*bJp*t&W3DhrLRl!n-iGl zX25KdJa@RWS%>y%^VECbLxWA1KP2t8EHM3a2eW+W=OAZO4rXjDpGv2b&3_fRO*r>b zu+{Gs&i-ofiNVib!0f*qL;mV)$kqnxuSw!0mxXf=1Y3Re1Dkyn*vkKAFwCW`lC59g zP1$brIe&FoMR)+s{ z`bMy=N%CJ#@|XeBo_Q$-TRraTY{<4}@8$H~fq8B@*v>uD*^sR-pYHT3v^QJdP6V?~ zSvOO_)^4XceFnHV#Ca`v7MSJG;`(F{G71by{8WKcV3yAqrw>64mfzvVu&DyK!z+Ex zwm6Y5ve-kP1GaJR*JcBKQDBw>*~Z%^oy|(H#lH?*5z;DsC8-Ot+4Kf43^s2%8*&He z%vXayCg&ChW(>{1HR0S@&W5~C(C>444az42o###k^V%KD;dZdKnft)~pfTOg!PW=k z8~z`A?;fYubpHL{CI^EcjZ+XZIT3Q`h+s<8AQ<8>b*LanNHho;gr-D0(jW*@bqG?8 zN$4OntwV!IR5tKoCd z74xXqi_hRLfRVC35g!68h6S+Kfu+GltT`{6cVNfR8f?UBllozGvRRM(8n`>DV%Yxg zMZMi%?J=aE0k7+yU~?Aixq5EU#V1it@hpMWcJWuiMy#^(|L>rSy&vxQMiGzLdXJ!s z9Yb&Ud#SDw!A5MG(}FIxerC|c>MO;3UeLw19}oM!e0{LF5%x7TGw8G6Y~1OLIk5Uf zZC?hvZ(oCbU9W&Soy~z(Sl5)|`4rwU@z!q^He%0>enGEJtZ^6jqpa#Z5msH=1Dqc0 z&w#ymofLGj-bE;eTY^44vHXiQM)LpLU^55yoO~eYV)w~h*z4;1u>0hrU@xvnF?fJrq)bG9` z8(%$lXsz&GZVKEh+5Zl%gqt`XhSev^KL&4=^rbMEeO4~^ee+}3ef1C6{rRcwDI>Pc z>Y$6=uCIb#L;T)f?GxHkn^^4?+vcEPQx|N+Ugt-`eiohpyWYlRul9Mi~T;K#yWi>cAxBH z9cQ(PvwnjWkJjYDa63}Pd@$_w>c?54pxC;KO0#jJ7sQ?Pj&z64GEIpo(xyN-a7vc9c{J0+V_l8yYF7Hmc*R-9vC z&+W;!K^J>`r@&ql?z9a)n-j~A*zrFP`}%z`*uMls^rI+-^ouwhGoB zO!`pR<2VdP%GxXTGtns6KBvI;c{i;0b@H^9z-?9~Rb zblLwp=ra<_r&xPt+58FiIQ}Kri+$}pA9S%}ektf;>#qj=^~9=I?D^Basi=1Ym{77l z6j#7a92*5)tT?4_0((!gO|TI=hV6r1l~^%|kHCl8J~r8F&YTzQ#oPk3y5=Pt)w>|r zJPsd{Y&M#i*DF7p!X#N+I>H(g)!PN`4y)ep!`>Gi8SIZvEc>Cb$LOc9@2xik`)R>m zto^q9H`@k3V(W8*F81~QymiVhO{^Hiw)rI3tPD2dol?D9&nnv9C2`hvxGLEUvJL*l z>IcPKA9S(yb<$6>4v$VOpJQNM`?9$**h~#J*Ta31%^zXSpGuA=VE5-Mu=nsE!JN)) z#2dm*9A5-o?03s;?eSrWE!fv2mi-`jT|Wey`e1W3+!Y^c?|HDV z*$aZbxPtSFd3?}U^1Bbdmj4-k4`NMX#UR!@0r}tPJH_uxbVw{4v2C^uHeG^^*m3R^ zY{b4c_X&D!V)++Wr5J_>8?nodw2r;l_q9grlo#86l6CCG?uV;k?Q1GIu7SO7{}T3d z{T!InnT^;!AFz&3v3))U+vj}PKA(i`vjw(Kv3|EoY%NNVm-0r|6|a_Cv#r)ZhvPn|HbydlXd*}g@<7) z|C6mJ_P+NT>xv)tKJX^kF-*4&WyOBCFxxt1#a@eEgm=YOaaR7O@UtMvF|%Wf-biG@j(}BZp-JXK^ObE`zJvc`*YhXgD!SoT^n?9 z|K$I%po=}176o1Gc>Wf2vEx}0bg{?o+dOaaj&GGA9S(aZ^*tW=wiq4o1lxW&$h0)4eyQ*#rBr<#Il#Z!aDY?u-Bzk zu>0Xl+h8vqK&sewnq9O-d~njw4Z7H$r`#KKvG#TH^RjhfSO)uku_EZLu=a0?c{N-? zdL73Xwt=-Cdf&3zYoo>}=xsZtwG>_Ky~_4MuL}B}K_3L~L_LaORM1Dmz86n`wI(RG z#$Y4X`vH}m7Hp>5M)v%l8jW12dY3YF{q#Gw=wH#sKcuq1y*FPQo5*i;U%pS!@7HXW zSpK(#yOS!0z2V(RWq&T*De)wjF39xRa8=^PFqplE5if(~bJlN*|0C~9IJ4m&vDRw& zKWa|C|53ds!X(-JKN%hg%jQXVPg2?Z8=jity)QS83m?Ed`w!?HgQt|V38UIcqyT?K2-DE>R((WJ89 z_<^F{uCUkb{otx(a|#S*^^SyJfz_71A1uBrawDAC@Xt-~I8yoF=%J!-w}5T46+9)` z%!K!W728|zzQnGwJ?0j%?FRcEHyZXecpZ#s7TfjkKv=OYg8Pul&u6guU;a0FxcF>k zN7(z}Jz-8~eg?tz(*V2PnXvjte*OgOU7!5F3ER)7FjD4cHGB}Pc=mdv_|C~uiRI@^ zSYsr<2-f@&Uk2Of%`j5tb2@CFZ^KpSilNP;`E{YPePH|C3%1V#Vf#D|#x(Oe46cV2 z=WOeVD@f)4K6LNHe-~`Twz)spJQQri-fui&or`!L+<3dp-YqzY< zz9**NWu6KnmGpnIFJaB;3nQ0mvG;m=sLN89*!s|*i>;3gy4W$CYaRb$-?M&f9slEl z{bkm%pB(J3wvPSOV1Ju+>}Le~JFH{h4EN%^`s7*bi4}hn$1B#ce;w8{o$TMUp13Ee z>^FR@=<^P+?YFd!jktTVud$AQvF-P>j-A;4PqU8wXt;{=if6L*#Oi0obE9?ar@^XM z_K(1xKl6jl0=QeUS#F)Y_!A9(USZ$rY5_iXbBFB~siv4Xb zxH9?a2ZP!5UJZLL9Rs^h&I&dSu=ikhTBm$7d}s1`_~XTWY7(6J=O3}(!`~cqvG-=b z4*HCs-x>7gpx+ntIYECY=wkQJBS9BipC9xEu*YJdb^3D=?B}w-27L+a&+q;n^wJZ> z=RRA*eN+D&0K0Ebgxx=4-)9?Pt<}0;Plc;st>JTE&#_0~3(z#Ls-GYTWB! zJ$I|@C|GN?>_@|%&p&~Gob;72nC)}Kj%Tx{ith%Ghc$l`TO+(Zth#Q4bste(ze~F6 z`YTM5`CJ0)+LXQu-ZgR0g@w<)uzem9^dsRR*sHEzB^%ZCC`^+1p9ia5vUxV>;{DN7 zZ^u6sb#;cT&}3f&&n1=3%3$*qtbS8nU&ERk;?kdsG3p4bPvoa_V)ai|vXNdL^cq-m zKze=9hr+rqD~3~HlB``L;lW974)$|k$98|P7kh61A?R~q&+W&8F7`F}WUyIe8_j=s z-Tb#sOwD^1zpq`@v-tZzea+vaZ_V^cJ*;+R-|d98o@DoK>vg=_-n zHe%nKCc%$Wm*P3#>0+M$8+Ht0&zbd}DRi-8Xdm=S*zb_L2E9A1u~S{U!@o%U=U}r4 z-XrO2gI-!(JjeBhG0FNztY>-o+$HE@>wCiP^W%bz*nN9i(8aaM=bb?pU!C+_pDkh# zdoCRv^s0A@za?G+Q#2n_*!{M*b>>Me>^XM0b;{HS`(vzQKNRl7`H387SWj&KV}srR zQ!MMNY1R?M>L2;}gLQZ=Y(I|$UF?2)+B$v~!@j?~ZXG{j`&n)s`*&ci^Qw2N_lo}M z40lIs;@A@&ka$e65pS9Fi9w$P`#HS@)^nWvd<}b^&s$N{C9X&|^I_jZ7r~xCe+%~F z^OOC7t%V=4_i~59x~^64v|uB?D(OAmFUpGDKfQx4w%#x3VqcHD1YPX-_p(ku4}!JF zn8-02-Z{l^oo&dAHJ23gte}g%p4=aFv9Ix`gD!Tzy%6*l;ir@TUw=@veFp3{-Vt=M z^}DUpmf3I@{HQI9U_T4I1iMdOwLLasU;F=rbx*A1SOd$y{H*t35l;nNmGtgG7rP&J zu}(Y#Vc+}qvQGI%?o+CVTSsXqYl7$bRj}VvEePwza<~Fp_5a&2Nfu`RlLgv3))rbg}lUvj2O~#nx8_U2H#}2VHFY$|Xhrh->hz_SVAQZ~p+U zPxanzd-7uKNfghcK^Je4^d&(T@0avWe=GV>>}zDppo_h?>=yK%LH{nSG1p#b57>L@ zx}eu5R=bA6@_8M{sj#1GE`pJ=F%|o>ziF`j&q%DY&9JZWhXOwZ+mBdf)jx}^Gq1#2 z>lDuyLH`O?KTEHBzVIXNiY7ll3i?^_sYzb~d!N|?Yre_P+i)LJ#k|Q2g-@~jyi?G{ zov>A1J%cXxetYMj*K?h!uTZ+UIxdRqX%8x?f0d zfvd#_^S+DE5wzwg&KF_N+ZI^yYJRrDeokz^S^jy1#$|iBf>d@@uu{d=8z#wgv9INR z)_e1e274|Y4}0&?0K4Atu;zwhn;i71u%GFg6DyttusAb7 z(AEE{>&L-H>~WcBo!BM?`)jRJR=gppV!PQo_S0dlpYs1(>xl=D%6>Vz))2Pr>-yO`W%mvC2U*9y4%T}-`9B&yj8t*nZkxpW zCH?K7i+wLx9dz-KWWUc##WTyHa7EG&hr#UrTMzFAD~79Jue~#2_erzuu@UzoRh-WT zUF>+?2)bDFT=t&?{sy-HHZK=_D^{P#e$$|fxui3FXwb#|(G~w?K^G5B_D=_WG3@)u z`>@}I_F9(LtGTfYtUgf;V_>gW4e-TD{}=|dc`kNaJ`K9qebVNY!d|?tjnAV9(owZIAy$lfC-$7}#w& z-ZtpsKFQCeK^O0n^rx)jQ>?wce7;~E9>!YdcTG#tCnuX>uND0~0Y=W|jM&eBKevwm zNwDunP4F1()y7R;FV?-za7EI$fx*nD*lp|@bg{>!5A5gh8rW;&8E_~3Xbwz(GoQpG z-hqtT+XU-=p?J3cd-3k5AMAL(3v)V)N9=e823_oUYGKE77|d(F%>QMud&wBGi*#)rUS!kU;Uj+N!{X)=}!oKIO2zo25{o6#2HZ4VLVz+Bk>-g^o`?+|l zpm&D7kKGOS^HCk_I1jTub&0*+o)mPk-?jcY=;LAc?WI8%d%e0j=wjaser28bXTb8K z_TFnfaSu|B*AjH^X+MPR=O4j-CG7VGYlB`|UaWf?z_`nu2K{JQ^I!TDSl8Wk9CzC$vDfN3K^J>}yCCRd$FMNyV)w~ktP|T} z*mL6r>(tw@SMmSiO<-=TtoE79u<1&wn7@RjRdRd+@hE_I1(z?c&*a6WDQfgI%^4>^Q|PyF08gRT~e4UG_NGWlw}%R_yEU0$BHGmAwXb z+1p{4y#vn5GVaZAPcjJA1GdkVu-j7kZvI(`%2vZJyBF-T`@k++ z3#%^GI~>;iNM)zOE_*BNvcHBg$zq)nFl90vC!ReKwQjdnd1tNo9Km8}WfjKO^YkZb`o?=wiQ5UKn(-=i8E?i+4$W)>~b)r7i6B zc@r3u>^Zt4+zXz_u^X&sQpJ3#Z4xu>GW{xax9i$qBlcQxThMOw>56BWbpjFZknF#)PG0O7HvYUA7qRESX4dhuIjp`?y$8TYkt()nu=-X! z)Aot&=Pv8`nGN^jy!^akJ+a&MM$p9-T>ENQ%NNC5>hfhVF5+3q=K}aDSoTA{%0J6f zY=^;)=O`E{o1bFWJ2dEG%}@C`0q&FdGPnYk&&jZ_sXJh=w~xVo5C1&uHT+%J`@_;d z^L~*3elSTEkJ$BA!+x)LAY6xy?5}}6&lkZiD|XpuV1GvNE__NVd(giMd$Hpm3HutK z9&E&3XZ{{^vHDH%ycKk@^%X%ETmLBN;vUJ*re7DaiEY1m(8Y%*`%_@uFEkcs2b%_1 zb6&CC2y5JBGdP#X5COhqXS-&x6(zdp`U% z=wkbSE$CwVZwdMu?(bvp$0b?nKwEU}tFEm=yTsBJb9d{+))Q7;vab%h_$(Ys|JA?q z&z;qWzlGHn@gmq`v=~Op`bX?I|7M-Kmcn0PBcD%y)3VUtAz5#2u@0;c+mCoZGEE!@ z2VLxSawzOICm>QSL`u5DCpv0$^Xoti|yx4>&(>_xEJ>7w|4E8uUogf!tIiuo-mm8gV=F?H|XL% z$^LlQ{eM!h5qmzL6?Czm0e>3wpTXXfG{f!jueo|(un{Xx#ectb+Vue3J?Sr5Cogtg zD}pX|dq1*{AMwk{&y($!XWu7L42xlsd>w!lTNB6g)+xIb9)PCU+OJ>OiyiYuK^LFG zS|mSbRW4t5@4FBF9-8zOFiEy2Y=!>>%l~~FEMNDxeO`ohFOdDau-p3u>~*-~hK1e@ z_MG1n-Z7OO0lWXNguNe_0o&*AV6R0BVTxvbB6b^}4!YQTlE1@_^JCcimWqv*ue&ez zg1xud8$K?@b|YMocv`TT13R9lg8q8YR|am|q43`Y-Y@w)2?n#ah`*cktKq7|_ri{C zA>1kHFT>u?Y`SsbN31^4xNL5nc_Q{bdRyz*OnWNtAKjxzzm@OtbnmOeMti4;9DBp+ z2hGWSTo#|Tu*O<8#|M2Fe00+1z}^!+V;jzi<+F+7S$J&XL7OaJcyOAZmu{NR2i1Ek zOp^7L*!T3`1icyVmHa#lpO?6{)l?0f4@K^I%!Bj`1-*S$fo<2=?j#5oi` z690bPKj5*s!L<>DNK^Bb*tfB zVEOE|dAe^>S8rHrqx2E5)?xkMPov?UNuL5&!A%^qVUny*=D=qrecLUPPx9NtUh4+J znLR$my~wDp<6zI>#qbVj^8YF9`*Oc63!l~SCCTP@Fqp+4{sAnX6SnNQHir)- zBmcu;kHwjAebOHd_Tqh$z9#76ill#IopzNv=j)qd_%5tDtp48>_WC~z9*CwGroxq^ zdM`H()*M#ZTa&J`GhxsF`LOpquO@qqQ44%|D*L0Yb3ZD32JH3rX4vhW6>NS3W1970 zGu#eVZ1)CTZ2kAvsrLa`@3<868}JdNs<+=Z%d_tQ%g=CF_a?Qm0akluKR&VS8-xA* zNtgYju=bO(e;+2v;uKfF^8cZA;`u1pZ?kPNmug|vE1z|+`*}#x6~j^RG0A5$tbUXJ zGE9=yyA1X_y^1b{UJ3i1;TG13r!!nfS;apbw*LohgD%!ROKq7K^v7VY;j69VQ>?ip zpWAI$w5u2FcJ+q6AE*g7wTV@>F6i~J_Y9+8PUqJ??0doG!Twr!18n4HTF_^}j$vld z?}6oC{kZ@p$@)|5etXtBZCnEHiH-bkxP7r7-VN@7*2FOs-Y@Yja7E(jFqrul``yUH zL4O4Hb@9G+>ir1bhq9`-PuJxO4?fprYpGRTebDx8e zvi2^4y>EE~?u9O&ZF&?jw1aIgR&26a-#UX(3A-OU!uGj6?ECv5*zd6qgfpL%t%LiL znaD8?mVd>3L9iJQd)#NkmvLS(4CtB9PucGSlVt6xh2=*!`&p;1!LaLUfR9WzPr!cO zc@0)uG>&h>?(+?L75k9QVcYkE{p|C7n4(z>jqK00-@r|&7Jc2`8{P^{F`ol#{SjXX zwV?6TWir@d8hZL%K@pAF0ZbMTIdKM(d_!Bdj` zq#X>kWI}9vEy+fITiJ!c?D)%WzoF zeHxeX))Q+ED9-8V9;4e~_s^_gFSh+6Sock}eQmH2dq277&P7|qj(Mnc`b6w|&~>oK zYbJbbitS&vCok6ete9*16!+gc*nM>(?EUS}VfWiq*zNiyjEihc#qE>No2?VubofAY z_1jx;1*zKmPOuSs?OmH#Hrwcsp>mU;?0v@A9V38NndHbj^A0s=OO57Z$-bNy=`Hn zY%YoEn#`szTm`Ewhr)_Y{KH^B6xN)P&k3;h39`8vRvX2)2K(vozG$-lJ6uVsweDTG zPtrT~&-aHayC+PN#Wo05f2!;uu*b9>*0?uu92NBAV9&8R@O#PT#_tyIWbTHMvwjx4 z{~rjt*mLahpo_gvSQ2!x?f)KhaiC z4k+e82N*dUM{)OL(?8f$2ODv}WOE|CE8N5}751KO2CQ*c3^QR}3z{>3fGbGVw-3X9 z?pvC4*}MdkWbrJ6b$!XtfL)9A?JQV+WIq-r$$W~pftxt4fW78S2{z(h$>w*k{oEgH z#J&%{8uVtyr2}O(E-mPqlZx$wU?V;gO)(rZuxQIrxIWo8!>U&{cL$r_kXNkZ=XKc6SzlYH{5SAH$VZYZs3szm(wGeC?VC@G~ z_8xdTsp>swP`(bx{wFX=7MoaeTQ-+kr>@Dc*Pq8=w|#+auooYY%C_6Pu&IQRGymdt z$!23%&vdG5_h2LLp7f#Ci9xKsoyc)|(8s|0C;hi@1*~@c&NhiXzJIcg&qc6(zG5AF zvHUb~yk;Hy*I|!EkM9@zpIu?Ms|I%e>;>Z@YrEJ!_YV5pu%6VSYkxJ73j0}p8tlFL zOTlIttoQ%&*?<3HEOv)|FRKlD9jtr0;vX9H;jriVsqon3^LZG|;up6|`pZFI23I8g z)u4<0EcPz!`^eY9My#@m=ifmWyRM4CMGRu=>j!;F7}H90^|O3-wGCrh1#2%MeNXu3 zq?(711)Bx1$NkSiUj%#K(*1zqzOXB-_~mCem?XO%YhYhr=fax9vbiwWOn`mO-U<6Y zJ|AwvR>$xI3;(CUNZDA3cT6@{Sf`CsV6Qn(!|wkVZ9^GxO)9(bLB+kYJFJ*h?=bkp z#1F%JC;kG~JW+f837^4*Df?3oF2-dvjFj1kkAx?3JOVe6%6{iVir998_f7f*u=bX+ zxe_MH{D}Sj^WmUB0#{)p`&X^g53j?ApsC&+h7|1@4l8!8SEFICOJ@fCyu=!>i(ow~ z$^V`3fW*sSUoWr0$XT4?ie&S5>%`Lndyl>Gq4`=NpGU$wB>VBOV{1&T_$LQ_Dy-{Q zvCV)zUa!JltKWk;oz25m*!#V1hZWyr83mt!jbc~;yWT~x`{Wr|b258BmTXk-hp^UL zmEHO9qP=}#q^w@C=9uCi3A+!kgbzJ2UWIuHEm4~g1wdSborm)JYk2Zt-Y+D6ut&q(jFiFNvS(_xR-ELi;@`@3O1>#5Hd!X#PDV%PQOpf7?| zPCgGgx_B3Q6zsA~S;KYh6Gmw$dJj^Sxd=X!RCT=v+kdOeGLB-!Bl|YT6!vGswr_wr zowZ%;{r1(#M*go2HdEm<(B$)9u*dY@wm}!~LE6MI@P~!X9xzhYCpECI&A~wz`<=#7 zK^M16K8ISTjbc6HD4y$K$8(cy(5Jz@QrX9X&4OU_IIKCZn4h%cFKkus`PLJApKyhB#&HVl z{ruzb{@5y>w!?}&&mb5npAWFt_Wgn`*7c=04}jGc@j_ZZ^v_~g*P#5bw4V44Qnm59 z6N_u-Z!l8USK@7w&4+L$ti8c!a5XHSJx(h0KCtgEN5h=X)&#Mi<1U9imu`dGVWYmC z32Qx9Jo91Q2c_$f57&$;mL*nO<}Ed^4|xx{~EX=>3hOp z7V{u@FswEx58i+gP5t7 z`RRB{;lDHNXOkboE;|%_wGJz`4yPu2co5f)=783yj_49@dM!<%7^sqE+29{*ysUH(^CPpmaZ_LZZSFT5u4 zjWC$CU3_TL-wL|8f6_aiR@jR*=jC(npo{C0ektsC0=EVmvFG0H)`@=>toIU%f3qJI z^JfbfDVuwpVeR2%Ga6PuXpL%s{XEhP>;9-YyZ|Q2%8GlzD*G;6K`NUS!KM}V{bjq+ zMSI2jV%x-V5`1jp2VsxPlfhoBby)T*U|n~L^#fS-O8?0Av|a2qv+LeHcNty*!Q5)nZ>wQ!s;LS z-wbxyEnuX4&cFvIo0DMeA)7dEwN2t)Nna3jvHN6c(8V5?H?7l_7I-{<6wlBxMV!ME zOCKHd2H0!zd9dR@AI3C`e?08H*{_4m?Y6;A+?G`Bx+Cb#u=bX!_i^jQ`4p`ADZLf0 zB-M4a64tdT{R_Acsm80@S;hE@Wv{w+vX0Nbu;<2^$wofUODvxk!QDvZb2?0twNcyx z%VxsaMO|XA$=AZ_E0w(t_S*QM?TKM7d`$AQ@z}zT*fDPrbg{>=$~u0;zNa5%9lv7R z54DcH_^ed#LhIzkJCLfcJ`B3pe!hU!&$8*(kk$%(_JsY8@g$hj+4VaTK0evchjp!~ zPo9G-lD;I^i`91Z?XqC=YOr}7J}i~p>71gU`@+cC7>Vs?Andw!w+(hRu>D*Ld%Pyw zhIWbVXKK*J)~5ws?0x3ru#+5FsMe^?FM=KvU!ti1!_ zNy&aLtk^W47r^Rs)wKln`uR4Dl$Cu4?v-pdIIk%C9oYBix}ev?Uf&vF`}`$rpVz~> zc2w_!u;>3W*zvy_>|cj>N%k9$%VSpj+rjSVUWujmgWZQG1p71Ls^sUYVE;?lF+Umf zXA&#U7htV3nx9|7Bw7D_1)m7Z=9u#feJCt@=@Vf0`6PH`(p%w;Vfp+VCdsY~vG2ug zE+};Uo~iD4^7$oPL8`Lh0RW|ZTiB8Cz}glU)M8W$Jv}%e&)d6 zBUN4R!)-~MI6j6+vY5qwzTSFX@&0)TY@bKLy;IqXU|kFHGb`B4h7U;kDj3ZCSJDrf zCyJ*Jd?u;lxe=})m7i(uD=HDsK$CsVkBhbXIJgsWdCZ=Ux(fQ>yIzmzA5bYemleRsrdWCBv~89uB*Rw+ENYo!$x(T2wy|m z#PK2QzWof=ILc=0pA`PLg^@D<;&!mgRs~(`XYd`ZQ*Uor{^hgUI{X#mqGuWDZ67Q8 zP^>jm`VV05kxsJ>y8NtbOY*O4b`-o(D%%kB@vzt4i(r4YayN{5)~?yGt~Ir53G6;u z25S$XetyIDw5tVfMpK-VFD$OX8(^f&r?>)^&zr1Mb{gCZO=Ta0UDrZbYn}9EL2pSc zy*06Xu7pXlxhJlIWz+GZ!e?jLeb^7?bY@=-Yfmftx?o=qtFNRV1y_)&?Z?0*S-XaY zJ?&6*zegPxY{Y(la!t^u2K{d9^vP`4&*>|yQ(r60>8uYsPAJ}M)xqw!`oxNPXwZkl zRcO*1gFZRfPlYvSnmBHSw@W-X*e`%JXVee>fJw4`SPg4WqO$!jF2=eVwvE`=#U9pa zPz~&T?8(+CG#u9bR6ZMmEX;B4K~vf%jb-sH^cTh7p~yE{5%4aWb^0;q>zosJB2`_hgDyTW>3jaHh;u*K z?W%*_uHmp_XbAS>VYjOpcDud`HvED3tl!kG%Eaocj<9`J!ERS~7}IR5YcJ+Ijo9j_ z|0&;<#Ype%M{_NV!v?w38++|V<$q!*EA~6~8L<3o{F-3xF`77*!2W#XP1xhR0^Wf0 z^0^XLoQk=0N&cO4>8HcCKLh5c^u1sEwCucff>HEUtHmVIer@yy#9 zwx4a_k(_Sg_-W7^;dV*C6xJFdKa=4NlYSixX7yeV`~60npBMcswx7zNi`ynY9jy1} z{TO^IL94xECl&qhW4H=U`c1IgH4Ro<6#p$SNfys^_-0u4yIoquBla^_UC_mj=TPg! zBlaF+EbMw4V6QD#+n&0_RiujN-k^(ppL!kEK3O)KT~@@rIjsGXVy=aC-O1)OxPnyu zKN{Bjk$w(LlIdda4}WHz_#5HN!I zr{2-9*XL_t#iq6Hm$pf)HA;2e9CUF-(r>kn&*`wL@>O+mwBJ|#*^0^YOAXUtt+MbiEVLu!8 znNq~u7xweZ{y`rM>w1yT^I*q+5&TZlPrka)e+2tFy)x+6!CJfI=LUEHX%okvgUw%I zmj2A9^EHLfD%fkqu0h`m{!y}Tg1rtu1|w(vBlbLe!aDu%6s$2-JpY0nPv2{cu|5U% zec@bK{V)69!0w0N!rogx5%i~F-(Ow``fG{h=WSU2HAbJoy-3w3ou?N2v3eLOYm2xd z*&G@4mM{l~qU+tG{NDh(-;Hbr|E{m>p9;Fz>-s<$cVV!wZW$h!Is_O-vlI%QkoEt8*b;4!e`KchAOyXfKuSnnlN*In?* zq|z(jFaCaPCm1PfqqsXP`+-3hcTM_HK^OZO;c8g#pyYo(thS4v4EAC@Ps#tAL2pU+ z6Vcxdy4ZdALD0ooZ{_C`>-5!1xD%TERD4jhOKd;u2fY&3^P~K1XB|IPu&<*U>)43x zXK(A+*TTMEUt}G7vFsI3W6;IEXWeNXKh5v~$^K*5*Y8T(a87KWp9fv+wy*zT(e_H% z-x1n6=wj_{)b>NI6N7mD6vI*0$v5-2lsR2$yA%6f)`6a|)`6kej3-qam%v(YHSVv# zHKg+YosSBA8yG2H)8KA6QQ5seF6PhKaAw0l;sK=cGXr*?%nUYnz`mB32b*_-jd)n{ zQ~ODAjT{IgXY;=f*1H$gbv|4{s~ZYzX>pC$z+QXnVD-P^IScl+Fat))+SmkZO_R+FK^JRHmR|8$ z-VbWaCb0bLe!4xZ{j=KA6Ru2pU)X!UQ(@2N(_l=pde4CM93lTV1e;Rb*mZpc``TP@by1gCF{rM#u-B!X;fhpO-(WA+{aW@zVL$(#5^PR|?dJ;E zey$2O@33EDiezg@J$nqzrHLHFyx-uY*n5mKtYahgH8s{c_6@M&k)I2#C+fWd6t`v~?vzs)ZT zKkLKxQwblP$_|4)Z^yvhl72OOP~xR9nE4m$os8P`srAGskt(+O>x+GzxJS~@gB7#d za6a5U={LcSZ5r$~=QS9U%wFvI@NUq>9_0`It ze*q(9x>&Kv&n7n(y4Y*T-mv|DAKp0G4+-{S_tg>BX?s2Fy~kCsu4Szc*Vrbpecl>$ zvDve=F<6 zvE->d2JL@lv;WccC|^@p)4Yb<>h%pj(}RAGb?n8yZ~h_ZVvo^NK^MF2i-Iopwf20_ z#rFS3(8bn22)fwyei?MJ{jc|VxL?5bvvJVH-v4YFbg}iWK^I%^9dxnvU4kxlJbMLQ zZ2Q4M7rR|Q2)fvQPO#2+4TE)W)tvv4^~8Qgm|z`yvF$Inj=fmA=KOWmvA-VHoR|MQ ztta;Tfro=G_Pm{M9X|_Ttyl8%H|vRQ|B`j=m%*OTZw9>u_PzBJ*kwPn4L-&DlBz8O zzsT?Dx~`9hk4pNzFqo}%Vzp8Be}uhf`zqLo{meMv%lvs$K97b;GCyKn%kpz((8Z2t zHmp6r%C^FaU;MG{Y5PjpWAt^hk{8$MlY%H^Y^bo5(RI=wj=0gD&1V*}nyQZEv*==fu8VR>1?% z)UF=?Ec!&OHBI^e>%=(__FUR4=!1g3zjeyC_?eM~vg4ree{sTtIj_%~h6i12eU9}y z?k|0+vo%omLt%=R+A_Js>IZ#RH5}HuB%21?po^_H23>4@YS6{jXTY_|e+#VnApfPl zMO(zytAZ}JUK@0=_5)elgFZayjX@XdUM%|=K^I$J09VO|_O`&fU&y}I_Vkt5eYFyH z8%zB{TVU%QgD$pS6?C!nYPdGV&;YybQ-i(O`kbJPtuG0>*nPD!=wj;~`xo(x-6z#S z7hA8l&b%7TT-6>-*VlQl_kojOJ-2H9KOW|@c)O&(0sFrEmTh|TcaUK14din*tm{SB z{@1oi?6PaEQ?~T4e9ckW&0&|_7Dmd}MzPCwwNBY8_%v)(_AbrQpNFzv+6G>60*bo}(|qTWUrDT`U`vXiV+R_wCv*A!(dVV4!VYzOO< z75@SwmH$<;QG0I>HnU)9vf2ILxgX{Cgh{g4=CPO4T+(IT;hp05cWSZm{qgW%Ul03U zd5m@984B-&57l)Etohc&@hjUT-ahG%23_p6`thKPeSi6D(3ilzKd!J&46SfC{40hH zzRCMjF>C~rWbGAuo_B#&ZzV_f#Io-l?8Q1Sy&Bg2OMmxuAgng3uKHjvw*64JJLgsQ zLfCC=gx#)d;2o3AgD{xIBX&PO5_GZW!~CF&wT7s!=YuY`{$9{q;jNSXdTWb%D`3ag z5%ykj8(4iS|9ipiw|!x^@l=@8S^Q#;(S<=5Z;<@_Jm_Lw@A7|T(8Zdc(wkt{+YCGY z$Ai7t`_IMJ885N-7=O2pPqD}AUF+Dl2K#>oeGROA^+b*hO6mWY#=Zlr&;F$E2z#vi z!P%TauZG>0Iv6SID{&`SZ9LvOK8L~Dd&}oJ))U+Q=hm^G1oukzcfp=N_t}PXb6`Ig zJQj5EE}T~kp9Fm+?D_Cb&`ay}TX!$m4yIVv7P0RUds-*9L9m|_hro`l-ZqpMd*5}k zb;=HheNSnCUG{w2P*&_U=NCa2cjdg=H7Dp_vA@dh-7H$2dCv8i_6pCuV%N2^b>is@ zd!PA3>-ZlU>`x53*nUp2j-Qcm56WqLFSia)4)#|EUF^9%%{qR>o+tNO$B)?c{yykp zwOxHO-#UI4z`6z%&!2<72==w}igj$nURyp6yxuLvdym~<&;J_OpV=J_`&vF4);>h> z{2V69=F+9G+dCEZ+Iv0R3me7xuH)!vy#s%Brp>;R;guTnPJ}TbnKVWzSsFw}eTuy2P$) zo1lw3VbjFXH|W*euQlg&?_SD%Tw@{DJpUo7VrXActVJDQUkiPLz6-3rRXhV>k}Mvv z=g;?oF8&!d@;?`@B-OjG1+bqtpMka3Nq;-p$p42hN#X**C!p;hv=8_hFJO2C@70Yq$zsHruvMHjHT(cmkSyPP3j^ z{UiNXK^Gr~CO==m?w@a9-A`p-*{;w#!tS?iVfWjv$wvLQ7pyripTlAGgSa8tsGrBf zUI(rY`VDXmwyO78xNj=^PuTbOHtmZ!&>qGl>mPCVWZygJVn5^W7xY%HJMHmQSNmJ@ z^+)k<1e0Vw#k;|ZzZUk~+dtTd`y`vQgUva?M%*>oObxo&^Y#YojN>%88}^Fr;bf!O z=ED`F8m9&DXi~-2dwLOD9~ddK5zAgS{b1b-#K*!6LuNA+b_^%N$l1PeIIQuV$T8k} zVvp(MpqCcr-;vYvY&DE&_M9u$yp>)bbg}h@po^_f4Z7I+9P4#_4$j)5d7}0xo>NH` z^M`N+>2(|*J5Ks`C9JhwHtm0v-!Ejd6|DD5;+co~l>I)i`d@qmyl>*Eu;zuXuW7J8 zYiQzl0k)s_V5BV0R`_68{jkfgi_cc}gYExFSY_pB6zo0EI2b9LKVsi2uY`52DYjX# zu5t0hu=kmNgpsncVqZHSz*Xq7-*84TpErhGZx^_N^NMGCm?ZNvo;jeHH3zEDJqLac zYYxljt?<&sV{gyD3n%?0c)z4~Zpz!PKI{$adY{PA2UfkZ?+24)^)8s$vTz9}^gd_m zr7a6DK-WC)cv&T?z#G0V{zWL^&#y`>`)gp5tj~V|yX*|uWt-tWuu+_E z27Lu=pC7;#oR`m!V3N$I*w1vIS?|rY3GaxFd~SP3zL!(kTG;j0!S+8Sv1|^9NizRO z!k+&_VV51ASpG-bo_fVzdmDrOWVjQy@_$RPpAI|DC*j&;)8@{iz3pJ+?Aot{yTht? zXX}Z5uN;i7b}80_;C7@<9ES#bvG~NaJVk%&9HpyTAKr31*=_q z{icX>5PWE|pKP7{6u3L7;<*p@Gr}YAPm^BLT=c(q|D<08D>l`2aj==jwNQmdIHg8( z&!s=W$Dk?B4tM1~J0#u!{Z4p`po?Ek`afXRrG4Nk+i?A^hQ}nI!|yI^PJ#7oJCWm; z@L{BCV+%Z#R591wQ~Z6Tsc=VpOP?{j=+9B^&u%0(&po?vF)mV#Ow(8w7nxhA~$Ii2|tYyYFNV_}Vj+I%iNinNL232n6Rl+@x6hj|4^NC?KZ2O_XemGo{?573$>2ObU`Ckz17r`1+>3@ew zvY5r*!>IegSoU*Zk}S@L;PqkYkHH!*@i(xaVfVVfh-d%ATBC*}mio4^@6Dtn)g44{Ki} zpO3>@^QA9_NwPh;Sn*4L-Fjk`mHuJS#hwqV;EH7PnQe%9HLT|^mECcxJZ8mE3%jm* z*w@tYFsHL|6ze{yvSVTOx#mX$tW^5=V1IGYFNGD4>bl%E#4{OoUDv_77TV+Z23Vin z$mh&pKMPi$%hz03?Uns}So2f%e}&~!b-e=nx@&c4(xX(gJ6%#-muyzKl{TZnIEy&^AWJ-o@~azj^X@ZFLu3G1$|9u*Hm<0*LT^5 z@tqB;|0iwhr?tK{f}Zw}GYTGwZhSKzKd47WP;i1G}$I4}3AK{Xi4P zG+1*(d>6cP;um1BR9VOK6kG}4N$0h~>IcQJ3a%nm+3&1Ze9qe$rdX!0X_tStEBlJ} z#plQqVD-QBi9sI}^!)-@uZU4vd~n}6@?GUDG9=5$HVq_sgm zBIu2<=78*P3;KefzY+8jZB)O>erI@{|D-FPGm@@;oB*$Dd(fu`y(#GPg8oF%Ul00Q zK`(7k^wm7(kM^93VHxc4t*A_MzBls+Ry>#OM8CkAOX6C%LQK2%gMBR=2J4>N#L*D= zmoQRx9knF}%{_{irlD)Er5K(KHe!vb{QNEGohhq+QrV^GdgfKxkKi3iWxv@5DQ1ke zhJ77X!JN)w5Gw|i9cZ2QifzA_b!^0{SN``8y4F2kkDrG%a4oMVfW|gVBa^yEPW5k$^WlmwL|?f1NL~$3T4H*KdP<=tkch8>GJcG^~Bwi{$9{q z;a*AKc%#r3V)M1%5nXdYKKFp#u03I|dq)KQNLXu~?9YOCCvD=G8EozhHjl&Z&lh1# zv$l)f=gWgWD8%*-x?|g@LlN61uw&~H^c`TwwlD11ejIEr3^q5wj_oekv56hqgF&w) zHjSIcZ!WrHdlPoQy#+hAFN6Ls*s*P~aS_`P*kuogZF4&8*e-@0o7k~k5%jtc+Z1%i zc0X+Y55bP@nV>%hyWaO<_gmLZin85d+w2Yd9(OqG*u;+Q_@IBqHLiZsoGEoK?3W&u z_Q`s%M%R8uHX8?K+a&RiBKuePZhkK^MF2Ia8PO<%T2)fvQz8~~j*kf@>&}Xri)|}LJGCZs~i|7-rtLleWVDAs# zfmLr4$9tjQew@n6{%5dk#FxM%S>GN@z1EKlF^tAWG0aG@G?P}5Uzzl(4fF4(S6!Wd z*SVFnn)3^;$^U=0nrriPHmTP9nyLBsAUa%^>!n}jpIKCsYR}TVQC{DolrAOJ7}j>k z>sdlNnEawm^54~-yjlKz*7_}SyQN#^d%)MX%KgmSI#0W7lk1DO&GQ|*AQq?zyc$8LMpTB!uOse=7+>q;yH|A;EoAUF+rsbdcOryN^c{Sa0e+{I{H{6`R zgYJ1tZr|J^Ki}9hkE^y<{{80%NYxK@JLKu;9dolt! zSNYMq{Y~Dlt<8Bo zgYM#c1M+iUOZHy<=-=i3p1MCjKjpzZt$Qd>XM8XBGicA;f8(J1@4M9eA@^IoU*2z1 z$yeid_``X*(U0W*7d@7LzB*@qK2KX7&&R9dlX*W(Na+mxOr`v+y1c$==so4{Pr1Ji z2j}y%^Plr_wJ9Bz(#a`pP3h)~^7v{<)vwKz*SIYqtsfFcHUCRL&+D(Bl%Jn` zX+ExlC+F>*bVaVuy)sXyOv&R~`Fws|jDInoH*-nV&eGMn-`Z>P@<(5r`yW0vZ~qih zts_&f%l&n^KKY}3mGm2PeKo1XV}YmTKP&|Pvb4gFL}+cDM?>Us($GGYMyp{ zEw^vDHLrgG`D)I$zMjwXS#RX!A9ypj8~>|3p2;nFeYMMTee$pKxH`_v{fwTK=clH$ z?Hzf3QTx7Y7vX>E`hC~dk}e_bOF9NWimQ=S@vcFyBAraWH)$8%8)`l5O*)Wt7^&i^H%|9`aqkKehRmw*4irT)K0{r^+?|2Xsi zZRP)Csq@>pU;lq5{y!3iqU-wg-_-QqjPu_O^WWC;-`4Tp$MEl(&!atx+rOja?>_r` z&;GvRcs@H-J^l{XH1bvC=aQ;_{G9`Tj_=Rp{TaDGd-Z3S{`|(DzxaK&-$$=W{p@$E ze*ak7An$*_-}1YvDs=U$-=owdJHH>OC9m=JbFZJ>ychQV)qAbs$-dP8yq}ykZp101 z1U4| z`{P_XFBLp=)CHr)3^@4gaih*X_xy(c-99_F>;K{0!2iR!-TvougGZfm{*MkAf9j}) zai@75ThO zGCk##b4OiZ%Q2^)J*w1y{J8!N=Z&R9mv$52Ib%zH8?^x?V~DU8{d+R`zoS|Jt8kop*U` zy{t{?G#`KDKbHkNy-M%-AMBcfonEc?{SS8YgPmTr5Bv{yuLip{*bVv*cAp13eN-_R zyKD^6_2YJILSx-teH>EvAN=hc?6f@9V`m@H-$B7nADtYHojyMCkIRe*b`!80^B?N% zLqW%<$FWB29^On!{^Rl0qq9`Cv15r}qyAe(>7D+$jsCUWZm{i2->v4)Ahs#h?(qM$ z+Xu<>ulAVgwGBJ8DNSv!hsFHI{w~Iu9^w5XyLm-;SzS7JC7R^wWZ1WNo6>G4rVNSB z>G)-`^O@0?wkgdC75F-8Dzoc+b(>N*cW&|}AHOfN``$0wlrC$w_qCr#V z-{UrIUwXfAx2|4E#VD}_+OmCV<+Y_!75awskGe=>r`Ivj->^H|m!3QaG5rMBmWq^h zP?%|z4Yg|8hUYn=lr%g#O8H=AbCB2Xi zv?=L>?&7CSNiPYDpEf1ky^5bUrR-(GI-yNzyLIzMV%yTH?A5mol-GLUALWm5hE(~} z%kmS;^3%)m50&MYl;uAt%j+vM{!x9M%kn+S^8L#4dz9t(F3TTSmai|%A77RqRhA!H zmj5Yv|CuMP;t9l)h8a`r{w<$7^Nz_sDl-{^?3t zue4cdW!d>PW%&)lVj+K3EdO9x{-v_~CuRA{ut+Js&C2p! z$q(D5daY!&w;C8qUd>c~fAaO={L$o(4*8KPn11BHp)CKiviucg`D@DZzal^GC;6Z5 z(Rlr??EDktr(9T^Zy`TB9fU6wzgEI*Rf8HmG^{aEt)6ApU5()r8C>rXW3KBD{$ zQnwv^8Qi0Pn6{smE~V3%fD5Y|FSIKA?!`%f4j1LpR#;SS^l81 z{PAV^Gs^NmDa&78mcP0ze@j{Z&a(VNW%(z{^3R4mUqV~^BKhJHUA6Y_b@AC5e>Ew%D+p4wakdN*A3HjK*OUPHLX!f&e?X~1% z`)(!Q+o!4T4)U>mza<~r_Xzoc!Cvn%YeN26@^vBq5_!%2{AbnLcgV9)uFHR^{JNi2 zYb)78$Nt%#{O;@J``Z%x^KZxhzia%z9q(_)JFW-cuJ8YEt?%Dn-``%}-(KI|0Igd4 z|Jwd-r~H*@)<8Xv3?x69k2eRQ=y~Z7@=uZPo$@~!bcaQ-gxdf%D9f7!e=kNjZr+B56@LwS6QIj^s_ZO?hd_ZImD z!QZDkAMzEv;vE#?{SJA>lgHbs)IEO%tnjku-@aw}0cH7p%JN5+w(meqNwwoBU_h+HdE>xAP&+ zhX(Es`nB$Setf5NF?s)Lx9%U`-Y@>U?iW`x{`ysV&xc#c``78eoez9}j?ZKNn?7H@ zoAKA5FYtW$19|@mgKy`q1 z|DQjcRk|{q*L=RdEPq>B{@${@zCSsbU+3=0{MYAWf8_ia^4(Hi-=Dlbha zXA;&wzK{KDH@nija?ZWy*ZcQ*pL2iSfMx$`OMA3`^+(F@0Q>!`lQ<%Ah~wCDl0n)( z1AGiDCkq6>47`kE+OISGYVcOD9FddqOTjH*-~WYR|0DAu?eA@9e}6;!(>d-PZ$AN! zw_gT}4JGYq``|vq(tbDCw}0&IVF# z2He+xiw(GOzj<>*`RxsOcLUZt?2W)=eSg$Y{!9ZtDGKhZzMIy7&uPH38gLu<{_EO| zAEpNj8p_`QmMe2-!7#!PZvuDNHLqg74S@Ystp8`v{qM8q{*pcRfA-SAX zpJr&r{`>^E@LE2TLjvkFJi^a_>I?o8xO?8uE0Q$Rj75DqR{+Q-vwo=MO~KjVP1D>p;8Op5@Ya1? zPleDEJWuL#Z6W_CzKUPE>F>*}{UmtJD``*r-yGz@2VUv?(`KIdEASr6cNM%1{OqIS z6}b{b#|!S@y1~8nzAu1}&T`kqOZ)r4<6IZlZtwq|;Ik67Lx%qgd^6W}Ei}wagKg(0 zYM;0Mz8t*&hH7PtwRZ`4{57rWe@lNBgOC3#QIq>Lq`j-btz*^7GuEFW@Xp)GAGZ0w z8NB0K=Wok;Yyoe(C!tr9%JX-DXB`;VPs_>pzADc%zwfvH90X6jD4L(c;KZlL^>ac} zACd?*bKTv0to}>E@@d4Q;AT9QO9T(PPhUy>#o#SBxlhyx?g8)M6EU6E|15ZaHJZ;` zz)#wJ6I1cWc7ofvHcj%Y<@w##DV@sp~RC+z*_fLj+OYDLcJ%6wf2-o>?x3{BgAJ@{s>7kriB z72v`e&o9%1>%cdhSFO19Z3>3L$Dl7>4~hS#b~E_NHa?eQ&u;-A`XuyW{ka3&@vys& zOWMB=e4Ojt9KSUOkAZjpYP|9Z>+cibwXN=&IjR2(@PU7FpYLhrJohtTR#4CLPYq5E zPU4D;V@DIUk8mzt>NkU@aINK!?S1Eg*D@ZlDP;cI!H2lkaJiN@2TQ;wT~w{Cw)(52 z{;R5$%sC_+PB*CY4Aa=kNminHwEj#N4SQvVD)bUcX55`kF5SZ;2mpR zd_va$A@IIOBmVq8SU#WijMaY{yyq2(+PCa|zX#8c&2JM26(qxo6|9=j`{R~O58-vWN%XAwVa06&ZTxJcV;3O0d{avy`p6Y1|(@PT<;Pe}hn z9^DN-9+M|u2XBgv@4I06H2FH{L)v>9eD|}7+MW9T=HNKk-@haDFolEnJGticLY@Dn z;FaKG%M-OPS^dkvT?^dj-GsiD$U*(Dp|4Dyyahay`5(6WqhR@L;G>G0f_H)a)xpx< z`@yZT`PmIFApa!nEcEjrcuK7NaqwFB3w28QnZeJ&E!-b8Me&Kj@4ydFt5!Z}?M>t2 zi=ABm`#D|D=HLSG6pI(j^K-yM=m(}j=jZj{`}^EyDP?>szYH{_@}>Yoqc_ zVE>5}8P87e@!g5qL2K^;@D*Hh`CaSJKJWw3D{M`BO+gjxua1`Y{|zK}O) z{pMhsJfDd4%_qTou4q+xAoKG@@cn1=2~izSQ}A_gfoqKar#?`oPTx_d9_1byh2mewu>S;5}T&%s;w5 zL-GsvL!GbhJ2BV*?%>+!8@0SCc(0V(kF3WIa7&rb6C?12Uv`1l^m30Lb!0vs1n<3* z&yy;DGzAC1o1W+8_q5j{QX4j5tDak zU?`_)53@+edl~qKtB~)uUQ5AyY2VT7wBUO1OyoOksO=TOiDo|SZS?;^@DZ-hcK$+h z@OR)n$XmKb|EC9!g4?gFhW-6l@c5wp1qIR{`Zw~$yeBj1vWwF)&HF(EEiP~2cHwP~`kNNmuqV}JL&j#P| zu|)0rhA#vkIk{T-H+%ka@MA~DLwUFi+=2bGz{;-!%ct{>+4Cvzvs`2RS?y0#a3gri zE8Hh+rT^=}%Q(;H?WqrdXI&cE%e%pAXSvU!%lbS9zT=(l^Pckj6X3$tkv}*NeiHrc z^~*270s53`+E0TVd>u;;KQF!)EvJy2ls$mZmU-AvHAECI9S(OTSEthf8=|( ziLLHayYl?w;BoxfgH5jgPl4n9#?PdDUP3>aApLt5JfCa$Z@2zTJs8p=`_hO$p`(t|W8Su0C zYg??nhrq2|TkZAJx4_%^RBM~PZycP+RxAH#_&E3o*J&>@e8L6P|9G_`0#4>*D)`CS z)k?eJ^T3bcf3_N)4Sx75k^Q>>Jif{6v*|&%VEFmnR=*E?=u$rGZ0oTeJpBdkv%AvX z`@q4}NWSj`??ivUU!Ui`GVlyO1dcb!NMD#W&^<(zLTfu?d ze<}UH1H2u7*6Hu*!7lKY9aa4#wT%BE@Y9xG(t>{fI(QG)uVW@FJ$)B^ES;!bW&QgZ z*xx@X?>`C-n2#6m&s6&Rd<4~+ZzXE)v-)R&1IF+Dy#)A`>z5$+reo9;U)8I9iCu*-io=E$@1NWYusBN_OpZqH1L(Cp{3HWK&Q)H#o ze+77dEZ%WBc*kq1m1Xw+E^s`)a}!wZ_oTZ)a})U=;4=Ed(Nj~f6+CubqIQwJ|I^?p z*uS`$x;~GAw`L=M>Iv}Cx4O??%liHZoWoyq^V1X@1E=Y4i@k3OjC0Hkl_NAIXs3WonE&!6J%lM?zn^);f8BYr*$JcG|6 z%+vPp=fKm6_j!471^6KG1aI$M1wMxS_3fp>NBQiC+!HG8m%(dZjs7;Wk)VI?iQsm$AHc`BFLWCGBJcYKct_0sJ_7ENc%ZfaM{wuw6Scb(HwPy}DE`w* z^85_&8sZx*R(>J)$&PAejy-=l_;lif=Nn!GKJf3{_X|OZKJEc`yuKQaKLeifnMi)z z1a7^iwe|qdi@mTJ+)jTSeKrT50{hQ!%6xwfJR?R=KM_nk>on`%2{6tK=-boBOTgnZ zs-eGr8Q9-PEbm(eK6HJec9}iD7TmoV`waRK`>YJ!2S5Lvm46t#ZDXQ#kKw-s&wooo zKP4yg^%!_9v_cAls=n-UrT-bYsitOPYjwaVSe}w(JZTf z4!Cy7Z_fiNzh4d>M?Smy(}FH=A+{fo1Rus9-eS++1l|(kr!C-Z*q8gPd>i-*K4a2q z_;cXn-@*Q}{(TL+7W-s{m46$&2KoP2hMxk@ydhB=QQRE-27LHWiP{?tpTLW^#o~u& zfoI_l`1s7_;I3aq>%9WJ7Jo#>FZ8eme8cV4isOImYj7v_skevU1MaxR@xQFsN5O}Q z=X-hZD0tV~5;f;vaK8xnl4<9K@jSVZ|LIt~_Z09};$6<3YYxr^@1XrNY`n9<+o3Pa z29*yB!TVkp_3wJ{fio`(_e%=k-R;PC7*^!TMsOGMwA;q_PVmlatCj2ReRqP#(5G&E z@Gp1`pFeq>mNy6E-~-I3w@-cxmiw$vp?oU$SwD}C%O}zuKhl5jIPrt2+TOI_T<~7Y z=aTtt2j7o=VOmsPtN^cl6Z&|HoBt#@$9*vrYk3Cj@5>hc*#Z{(MdnBBxsQSoW%?uK z_ey>2;|~5J;06w%KikJKtyaRdc=%Xq4Pw@V$=np?rk$0ydcsAjWf5P7P z5^x9cLpL911{Z_nQ*ScA!jFrFV4hQ zhdd2IGWuTyKK4H1kJkUUfDWIw(G+!3R%4}-5r zRx3-a{-?p?<%E9DUfRDO?0;nb_JQ}T8?O`?pY(qmT(~e%bNaqH_yc$o@{}+j<em6{fiB+1wV_w51Ue-_!{`h-POut z+Fny|8#wMC|BXCk*^5V$Yyh9fk8kg^|5N0ARgPePwfFxHM0k-T|6crE^`=yTzpnc#z89IrfJ^PK?8XS=6^#Xsx-?;Ugd z*Ydtq;AOD{a!k>n(1IPKR4BoU1 z`Hn&sdf5tI%XmAq{S$(ZgL8aNZZTNq>xLKJqv#<1@C=PW7x2^Y^Kq@;9Q+m> zj79o9czqCbU?0E8%BO?nbJBBdzAgdFC#xw|epmn=|0wYdtKSXY{-yDXv@hdJftO|6 z=L)628^ImW^9v~#dfoy)>8sUBpVhwuyqVAbq1N^JPl0!m55;m*e|qp`@O0#-lc&wW z;eo+VGB|pW|3{p;m6kH3QL456>t$YN$ z_PvpPa|`%}4GH}|fy~dH;Pw|)E8l2xOAKVQ;2|)_K{uF%k z^ovx!$oqaT^~u-ROu6XCU=jA{yT>c*wf+ggi@|%y57=hUpAWv_@9_tl+@$_fFJBFk9;|n_JKl;M0*RE&(4U{xd`S(;W1GXOaJOjo}hF&M)r)7Z{(&D|vo9cuP#5eF6L!_g|f7 z&p!e_GOt?scg5^qf#dc^6}{*B;EV)5u3!3Ta8 z*@tfh&yV@rp8)TS#pCyYXMV)R+k~FJ3GUs(=bN>E-l@TT;46s#dVBOi@cws3{>J|U@15)IsWXE^;O>~bItupR z84Rj1jqT~)8L)NGo8KN9P96kfH%eL?I{Sx9h)QinFfyUr_BUk z5sO#97TgN`P1F9I7%T-peCkEDe*ug9z5%>9=8wJ&{MZK~{dg;Q{Z(`A?Bw zY5n^mc)Tg1$Nk{F|G@r}@pBb?yj-ok+3NoeJZlsAy;dF&nBDxxXg_2+cnJIODXV`D z`0yvkE8j3Y58Q!0_Y%Vk!T$bdq0cwT^T z?{|TB{Y|3QWAFQ0@TNB=YJ08zy;2`}e!k&H!4Jpc$4`KRKScQ^KL>YitybP{`ttYS z?Zh9%K9Ti5X%+N~{hT!XGVqRjs-gV44BSDy>vYOxK9+*-z+R9Eljk$w1K5j@255Rv z0&gPUC!jsCf5*U6I4^>l01>YO$Lal(;5{+@`X%s|-iV$KfOo!{{T@0X^miD1kp4UV zCLRiIL4JS2-hVoTF8WPc5q`fI{A{T2P76B055K)y8L;`j3S5ZMUqARs?A>qK{NDub z`T+XE@Xg@Xn0>bmEctD)C+(dc+zpP8?;-FVF?;PX@KNGR&R=T^egIxuZmrGN@l6kY z1#W+W{2N=ZW8huy#@|rPtY5|awo#u}WIfIRcf|4`Uk=_*e$-`@%lga*AN(-#(8jw0 zyyYEHzDWvvl=BIWp0TIFcZ`o$?$+mAf8@EzzECqJQ>)$kkkf}?NZ_27N%r=Mo+%>$3a z?_NGE0B?)wuQ!6HyoUE#f0E!$%@I8o!O!B)onh~LH+UK2?NHnld=%Uoi@$vyJoO`q zn)8pEgNMNTSI6iRd=U9}63@$cehTg+{&=Ca|4Z<6&Ubvx+B>-i{tD@FN^mB4Va%SG z1K#!&&h2$PWN2y>9?_;m>>d{c-RWiD*A*H~8sdwQ`?6 zk9`D=+cQVN2eH5M`o89%sh9cqLN(OqXM>N&&aYhzj_bR{;G^u{weV-*|2Kk1iI)QD zALk9EKK$e4$+Vydo_W$mwO1N_Zw604h4{2R|1t1Z^7(!N7Wwfx@D=E{FDq^iz7F1# zu2z0y^!+oz?;5Y{q+I01aq#Y6j)(bX&%c`fp%0~8-gh>5JZ3*7z%BnVUU{LtuM^yQ z2lQtBy9zuL`7=+SZw}JnXVFjV?0rS>-7$aUL*PSis8&v}_kA2Z{!kQ;_&ad-oz)6# zp#6CaycPK^^Dq4SBzVSU)rx!q1JRh^CYT&i(Z!lCH@97xIL&UExw&yPZkKa0O z`S2$MbHT^hk2z-TEe7w3`F|;J7x`fnYx{%X8)Edi1w7;S@ygHb`5oZR>~C#0{AKXo zrSu1dE%X04m>z`m`82r9{G6rrn*(utcWjCB$IrZ$=ifG7*<|B67km(Vev7?-9{AXm z)ylZxh2U1s3*9(X+B-Ao22X*Xr&)O#eBgVL{apsf&#P<)x4g1i`JUDP0(fogJi#O2 zZNzV0qvg%PKZECUKGLmMbMRg8jM)4flln1vKV=R21o?KVJ%0iCq*%VqCE#uOL~R8I zrmXkt!8!7SWZGoCuL5^0M!%w9M8CdSF#2i=<&uAq2X_(=d$HBuEah9BJuLHoCwT8h z<{$bM{26ff3x84xW}OT^DYzHh9+L-O2OnVnPR`Fs{U3nKZ;IsiZ@@Rai1@0F=d?G$ zuMdug`A8*Mo0f%y|-{?<{yec%ju_4{rT6{xbqr z=HoVSdRr8~`5SPYJ|B_iWAVZ7f%mX~_<4KZ&%v8w^6U5DIDJ0}@ zBX0egf{VZhV$XMh556?gw>No2Crp5_5y4F7I1()1-UZ6#B;z2;ztE5 zzYjdMzgltocf@bNr=J_?yQ(}NU6{ zgXxsZ`gVeMe?C!rt=8xKE4cIhDSi&ufg5K^UpRsjljoY;;k<eMrh<^LGz;>wDRcv-SEX@X*mHzx4ay z_=%M(URMqCL1uyD=Sfyb{n6YKr1*aOllu~#>N z_r?6HJHh@(%0DO1V=p`YpB8)tJfC>k$EYOhdkB1hc)5^?tnYE~-qu9zvsQj0hST25 zkq0ku z$B3_Ws=S*XJTD3T6F-yr68Z5maCgjpZv&S(54yz4mw_Mn@OZ_oZ&R=a+{%8j=a($l z|44hcfTvs@`Rm)jd&r00LnV>dp9J51S#&=CbKvng)ygR4(w~RHJ7fCd>)@lLu7_8*Cwi}y4KUjetU zpX=sxT5tfo^G@Ue@=30|5-G@FGxY($j>p#MSh+G-p2Ww4zSGU zZ1B{;+vmTjT?v->VeIAH^Sep0GB;C&~!_n!wo_DG~}W`lRe@_`nDJ3qnxrM<5Q{21|5Pe1FxYhG8a zG;2Bj9(XtBnF{v)4};I=eBW*>-vz$mD-pka0ep8XzxrWt0e@}=^<_Q213r#D^@P3e z2jIi>&&#VL;M1`$+pK?o68vnG@7UZA`4PW9hjPi!I}dz6@=5fi+DG86?B{(zG5P%9 zZTO2XH{1)Je+%(2Yd-}}vtR4xgZLPD3iN)Fz3(>g9qh+B{n;Gs06)Na&Q12d`@nk` zujl7Sz*k&ctsH4`>+wzS%w6MQzS7UZE$B}kr~mM$;F!O2MjC&iJyH9f_OCg3B^U`4 z();D$O~`l8|0}>P#CN2Yj6Vg=v3?@oh2C!j7qCxdzN9=4?!73=*L|lvkN@Px+Z22R zyf-EvJ_}wu5ak;^1U?vxhkOe>9eI8d?-zaYWALW;R4cfN+WxP>m#jVyFT{=aj123I zzkZ>Xb6x>FmGe`3!Qv0j0Uts>IDUM7umHU6z9_$Uh2YOb^5o6nEIus|Ab)jOB|jSTnUcbN7sU%eoeLVIMrnR z)`5rcf94q81l|X|e$(*n;KJ9Vc+BU)H{egSXnAw+DEJ`uf`9%g@FlVQsb|1L_f*64 zUQ-9TcQod&Tms%34|idI8vm~V3;#^9_b0)#I3IbI;UVxL&g(s`82JF+iavXZmEQ_(Z>d(^W956m zt#5StNyhV!VDUfXeN%%of+wUr*51?LheLXQUhrG+p;EQ-QtRK;Eb=OL9^wLUEBV-; zu=Xzl7haD1ImwN;8+@Gn4v~lA|Mh{lW8XOY40-||xH(b#gUOf8;4$Q#lmFz`f_H}N z-yGZl-opO#71rJt!QbJ$9*^_>>A^pNJMiBw)bgg_hu}?rJ6^fzWJkaM0q$BK-T(6p zcqaaiqnD=O)EkknjCYAWe>QmUjnREJv%x#b2leu48F>7S)ygS0KW_ndqwmYwpEH9I za0~gN^X+}_25+aoBHyLI?+4#c{(-kg?gVdoK}0WigNHsA?Z4j#-WrQXJPtmJ{dtzj z_aA_Fkbjxf_Q;O{-_QQWJZt~Y;OWGR9DO$hFI~s;*RQ;f7r4KtL-7g0JaB?| z)nQ2P@wMo-s=NQE_u{JNS_0J4im=UErzXQG8(!_&EO1tL^!J z1`lnE@;AN*j>qeM3BH^7lbf%m;Dj9W3;#O)KQTBBy#MX!f1{_@fD`v|-qz;l3h=J$ zqI}S9@UrhA->v?e!TUZi9^R)=1RwlCqIRE+=Y8PrQ;0`Qk@Y(%_&7LdX*!Sps{Z^E z_z?OorS+SFN5Q*(ny9(^QJRCNz+LZ-@?D+=C%(dYRimGyVE@y?FDDHnPh$4{i^2P2 z`A`>v{Z9+Ow1E$BzShmh^x(DNcz(cIDQCWCQCH;o82H(LkL>kr;OXc~nRY4v6nHQ6 z=IL`Ucw0>0{|or&FCzQom*BP7PhKAX5#0UuD4z3z5z1rvbLW6_z3l(#`ZNda;GyYJ zK6MxPz{x+UY@$Ch-!}-3@%uXP=9s;|9vrt%wt(aQ>feDMKwf)!`5<`8s}i;Uvi1MI zJ}>Ve&z1ij0dM7d8jth4`0sxP=io0dpH9z9&B%T_2i$>u=j z|3~m+^AokF3_k@PN1r?&Ec)cP-~#gPRa)K@oOToRfqXvIa0|GryBfyFUkjdsJQaB- z{aFS+GZyc^PM-f(oqKVAyriwIb#5yq?Xz23Coh@!%$&B?R(WPV z|I|NYB@+wu{gZaUmrQ(SUi))3bJ@J-D)9rg;gkxQO#e`_P|WvbO37k=nC_-B{mJ3} zLdkx0e;g1--lSYM`s``xOP`lY!N&y+IdWI;Ms zN)ByMR4NrFmg#g&{IfirDitSrnUoBsveNa2W~9VA#-#l><+*%*lAg%pehr`V`O=2T z-zzI(B{mZS8<^vff$SiEmN^_P$b=@dBeGEOIo3bTcY|>xBz5NLAKY*RUmD~Jx@vuS zUMRn3M@r??NWU~YoXhql2c&BSp*q@KpOilh=hJa`(`cr+k>>{Tfh=%x7=A5UrL^F& zWK<JE^s->o7hPSbFIbcx z?#n0JI4voHpg`-qGc}TO|4oi)H~NMLv?U?QOj=q+K=5B9^1+-i0W!km2*25jievpF z!v2U2_g{vQ6^;}+0$=rKN+Ng!ZOCRu_-7!SW1Osp{TU%p7%gX#g_6*TR2<1?OPOS* z#Dt~FGD+F2yu55piCnUN$gkEBHx9%THHB$yq86yU@RKwMd#OA~j*JfbB`gKepOHZ~ z#b7DL5@eF4EU|{DQocNtDbmwP-xd8?-xb05yQ06kcbSwyVA6XLn?^G7YiZ+1c?dCT zC7H3zSUEG29?6Svv_B4HQsvQNW;j(SxQUeqa|Kp8x3QF!1#o}pZ;M`(AEt&Zs}!Y1 z%XvhyOtZlL;gU^%-v4PZHCifVQzLEB%Zi!)Q7Lv}JUK8rBCi|J#Y*=l=}&&Wl7+l7 zoyny(+Ot$~Q~*}lYm24p=JeAW7q3~AEc6HN-;(o8Aeim>kzbVH^`miKCF(EVF-Sens4tS=O^?zdE%{X570ZMT2t zx_>ic{ryapz0kU+@5o8_+;7r7{Vk>4^S{zP{gv+NzXd{kSHlspUV7A*z=!)xR!{iU|6PCxfgjnsIg-(=m9_DQ z#mkaQRxenwIJt1~QnWNrwDZ?R@oJX|y}(uFOqy5CY2{UOf_&eNnf@}k{nF%oeRb5Y z{;*&6ro}5e<;`IS3`_gcSA){9>x=ziP7sNONULXVGUAI-aXFhG5vEz6&n5fIMK4)H z7|AuKy_^N4qIk|_!HVpd{yMiU&|+Sc;|Xg9$)D^3N|p03y{xr8IlH}$qv}$HnxYfq zFAXte{ixGoO0}no*vy#zlT4NUZ^`~tj^27aj4;UMRRBYl7a?^}TIrnf)Ei;W1oEpXCSNh7hN&1bGCRotJ0w*sqgQgd0JF=qAitR? z&&DhRnp8t7i<)vK34e+7i)o4?>lip~C8E_6r93G_)GtpLGbz!UW_-5=k?CZ=y4ROP z%!(@3euW^NDyJenR?<#|uijA1>NI098|k`M=FJP%r*heJvWRX{>!y%w_kSzL{&r~~ z#igRGl~_-#XE7NOpv*_MB>F=}6i$OqjQ0x!q%(b^Vk@{c7+jw!szFh|lK3Im6rz6O z^PC(WklAT$JoaZ<9D{BwAyjM4iZIe5i!C*p&gL<6g?i9pPTxvL`E^|`TPlmyYoi*@ zs?#)DPSOYd#xFu^{ln5Nfa|QIO@U%OhNwSP5T)*F7SNu32=ICY@yF^ozlZl=TZ|w6AR=okvA^RX*t!G%SelANQbtC_#l}g)+Qw^%3y!LSTC88 z_1U7jox-G9LQFWsNkr1LNA?Zx9I9ezxNtR() z>4SJZ17jnlWwT9l2wE7zIZr{ixa)zLs@d{#d8;Zb$IhOI zJ6B!1a>epBi&rOCsQZxYp&QDSr9kQ=5$Gs$NHAD65g98M)g+#A zX@m1Rb3*E&7?L-K(ytV8Fn=>)?%OROVr9Wpo~z7IZwhM7|0O9N^e}G=AcxdoW+5{l zhzo_PPIhH*BGAIguF`4|foSSTMFhQT7Oh;K>~_*q?XakStd6qtXf7C(NC@x7sL_R! z;rQisvmuJyoLw)Z-RiqyN3%&rQb-}DXD9PT79#BA>g?c9IXNfUn#?)+a&EmzB|N5d zre1oNFfmmk74k(#4y#wKNG`)?MPMaox3%hgtj-V+=%?2!#pUYGZwQoR!!`Fdk-BUe zuEBnAR*ehS_%Q}dP#xPhydfjf;2KWNcPbK6+19ZI#{@3 zi6;_$Q>k1W=^rjI$ya%1Rhuv-5zwK7BZR$5Ls2eb|jq{3+Ng2>gJnx?CMovM})7$PVsCDl@=W5jO5GNfh@FY+LlrC4mZ}73!I>G zgD#;8GH%=>=}xg1BJsw828HD#h1IknfecTL#Y`cuBw30qXwShqxzaJ>6{2<2>~S(E zKY%HfN+%21LWZtnVgzCG&e$-i{Scvy!;7x!AHw~`wiq43?@II9(mEZOv?YBCmMrO= zJG(8}1F@Hvq=YcFyN(aT<{SynX(VBc>9Ve|L^W9n30&&J$0pCjXQ>|v&_ARFsoY>b zS>A|UAYQHhiLN-SP32W6A7V4AHk}$?N^^_w$Z?y+@>`V3^^dw=8-zkAx{4WRIIfVW z7^6sDU2uPJ4X9A-AGUa?F|ij#$Zv6uw2sD0RcY{&Ic=ggM4hPi5_<7G?--@-FPRbL zZok;Vmhwu+Y6TQ^P$*}KyJY$y%6yhS_N8!@V_f2wSQtfHl%|*Z*h8bGPD3b8Aw8w5 z9(l^F=TI+Aq>~NSY#8a2g(;!*3H69Ut;!n_qAIZnXA&V~0Z|Noy5f?1iSH@&m>f=v z$2b2(qG$nEV#FS8vE|m*JzBsemtzR8S<#j-AoyOGPh0Rq4sij4%J&ARf z?9cH(e5*msNM=NJRCayFbvSQ8j4q z_3WCpdhHsM7GbnMrg7^GtV`<@{1RezqPL|Bode}X8Uj{17piCT=;DT<83<~zIm(Ra@89y}?TC_ti_fI-y$> z7{){IQg0&|6FLPa^XG)wYAmz`;#38m_r(45YEpQqL8HhBJVUc>f*6i%mAH_WGMOo2 ziIq4K^E$-ML^7gMim~Jhc~LSSN zGQJ)Ou=A|%Y=Jp)RuwVG{<1h&ZRx(=RA0EU0(+|#FbgjfLSo)7%9pSksEvgwTtqAk zWrYWr#6n01*AUKIF`9E0K#BMug0qy%muL4645Hjsgo-ieZ2tNVHwe{eH!&_S)x?Vt zQXL1(PPUqr+^(|xxvXTwTST=IVTsYl(#YXcsV_#T^f-)6Mo-53H5t9fEYVIV(n)}B zCa$j2?c#A1)ycv*)_t0xRB6b`1G7;=;kzVNTs)HY!q;?;=Zg^@4rvz2E80I(?9Dsd zQi)q6Y7cwGc#yfp{X_ZHne?a%$w}0y!~nvsdhcj2s^hL%)pa$o>qWgd>lo6D7WecdXA@PNCn2QS@Qu(Dy_Ti(ku0aA zSB?(%Ws0i?l%6wUfdxr?^+|Pslk#DlN(fqv97n*z1z4fa+3_laRxIdUmh3DQy3%cq z?sKZTl6~t&3n~O7=2fRZwsZ4bu>2*iynN5Z>0(So7swP)s;*~#7yeN$KpQ$0yE>z;k;<9K)|dTKm9GK8 zI32wUsbF80F)c&uyFW@*i6^84Znd-P=d>-{IHyfiMNjAqF6}b4)Tib`kkqURT%Mxo z8iLTWTQn)riIi}9X>`QJVAByTWBZGnvXNMxSv+`~>j)b64-X6@J;nY}MJ#b0@2aYD zG#%uvF}+J!`;baP5{Q=LZ80WKmg*SW9_!LQTjB!w#MU}e{0r+X3BgqN0zp%RzP zuCBCg6AB#@CU-Uf(L1F`wHae{Y&u%@<=v7yRV8HXN#HLUL!zwBVi8KrWyWL_bW#nO zPykEMLI+91U@0}vhf@-)$K;Hn9CJepUWm$%$2zEiOy_R+F3RMF0UMc^Yf`z<43@Hn z0x(y?0AgpRKRc{3iF!KrPL8Ktc{1X1CQVP(R_}qSil6G(m-t zBY_)72ZaEI-5p<9a8f!do1D+#^2KDZ2uq6$#wN$9EFsZ;9Wy>jGRfSpFJU8&9Om4G++*UjeR5oWEC-H0qOstiHIqzZ<;>BukMo6wZUn&c=LGHMHIbk)I*rTB{ zH6wAHWImlYOIS=i8n@=MRO`v@(Ab=NvvWY|3}9C|nJNyF8NB3Br{Lr|Y zdv~Ka%XqIk&?s8Bvphm0A&m1b!blK%T-B@S%mtSGKqw1cjKEDUVsDLTOL3v*3)M$_ z;S!Uiy3ao+Gz3(a>I&*;vuXARbVtXf1Ib2-f>&~KNcWFso zNYDAHkquBs0b;$$XqRTnrTG%aNha6yCP{Qy+}*o;)yib&;+{o4$)svLnT<}#(dd%U z+g}Lh+5#lgrMwMa%`OISI<(G*6(ZK948$)Hv585(ytAD5*8dXk;;Sr+F@a1MR5hHE zh>FNxA7T_Fu2VgI|-dC?k=U5lf+f*lKpMBbtIBF z6k|gEJ?w$?NDx$PmX&>O|E69bIDY}UaZ%d^gZ-+uM5S0*Lj7^)-wrCPr{gAVYzHjj zY$yADSKKB6j-u@4^g;>DB603f3@>p>>sU^7Zfp*j{m}xhP$5|!C`Q~{KLqD3H%Jo^ zxv5e=(pTazB85ankaHQ4n~c#cAseb1(i(Ovq^sH) z-MMlKIak(|4M$JdMYcypNnjVmoVdBhilw1cF_W%yxxL|RaSw#uQrKYjJn>I5tUDX9 zF2Wc0rQLWW(jVz|(XWuNy7xum?URw0hA}4+z34e_e5BY75R=y#s8RH*AG;9qD&C9# zE)wlnkJ`v#D8NmMaXlMGkwC|8@6TmOV6tIqAhVcu(Y|`SKz1)w8pg#{ilC>6$%d-P z%g)T=5_YC8k<;N}EF!XQEMyOBXOd2gmi}9RL=DtQT&DOAl1y+IL>IaV3B*}iLiv;N zjdG%-(PaqbxR}^N_AI;<4qa(Cw-c68ti0h&=%j_4X!Ba@snjOA5Jrq5Zu9h`QIR^c zT7=_>C6ku4jqYMbyx&E_Z{nF?XhFDY=9+HSNM<=ROm>CX>>JoClri{~c57mr5#HjR zNVa`EPBslu7Oym%AFY83%L%ic@IslbNU^Z$)HW*L8!j>mZ#Gd+iOKnvvi__Y*{8MCm%xxSLcS-hoQblpo7qIsfIsl@XR}FVr%r;zUx4x2CL2yVO)%}%Zadf0%7_u)S z%CC-Sq@SvyJR2(EXyB?|AjdJguhOhipOew3thTxH>h_vcjbzG> z@I*C~7GTpAMXL7Zb>~$#PbG!f?PIE_(Qs6K4)eq{WGjPcNkOxY+&-KP7g6nY^ki<= zO&EY@M@_ekubgc7*PA}d#d$}^OfgF+g1C*jou}S_E_6S`XVaq`tk%u)dLoHA^pi|q za?&6G6t5v}8H?@^>)Y$8RIJ;iOX6&Bc(HQoGUe2DASVUMogKlOEtS(m)$yL`v9u6H z35l+nw)1HUF_+Ov+DScZFQd#Xs`oA|`Xqs1#4(A@^a8(0&5cHd%RZDQjtD_o7*gqj zJOzn&K)mMlI$ks7hvDmuP}?IBigT+Rignf5u=}7@af-svwA+cV=arZZG5JKtt&aM| zk!ZhAMV&`vT^Bn-9V18Xgw}nAe>^a~!HGS|n~KqI4>RDYSGp%rS+ytF2#!?~Q%yZG zx7+O+4)C4ibdcM+7j-j1S;^}=GXtqn@@Skb7cA^t)FAajuP{D8%D`<)*dcaK5WJjA z6Pgu!bb`1y3qXTr&k=G&7z$~IQcpL=ofuVXy-tah^S$gd&z`q#*;~Wy6KALi#h68| z^d?(mNoR1t4%+GVSf@qg_K2P&bx0#Iva@CrfmDwhMRlHyId*S`qk~u*cJm? z^XOfuqD~cBA5f{1mfDr-W7jk06vfXyDNE|2tF0%p(2va%pSpOGnJiL8DPQYjze$)M zm$ncT)5Rpj?&OuTdh0sm*n*rGHmSvFUr;JUjb3&c#?EOM4FG!Bi^>evV zF$YRv-$gRL ztl2|#n<0K*VG@lL<5%a6N(PSxqy11;WKts~5{&A$gR)|~`{?GHzKv@_ZLN#O=^5vx zM%FobmSPl>;V4_PON%1k9E5b;WzYFpV{dGp-VFohVF5UDB(Hf9@7PmZF93>Rj)cF z*e;#w>Yl%@XXCJKZjta+TJ1vpL{)LsNLLA2MT1eoae6b>b6g0#`ax}+rBwqo`fLsOP#%S|II^*-` zNy)|gf#bDvZcv%Q_7$&U_uA$)PD}#ihJBb3*p;si+chKs+nB z!|(YMdh<$7JC`Z1)PzwDjoFSmFATFC7ifm_a-U)`shKa)2IY9?u&Sl6nncI~sp7&H zPM~SV;zl9UC;?b$1!5xCRqQNvXPj(?Hryn*F^j-*e8SlnJ3tbU+AJBnxzKwEgOB2C zk7BGKGYWE)%Z%;`S*GUK#?ujSEY$e$)TKJhWmn)rH@NIVSWY3kUOSm=6t1K3a#6zu zA{C53?R1I@0Xb@cK*h;+xjrhq7H~99wm~$Qkm8VT&hiIi%hVHzf<_@v03KaKEI3L} z(O`~W(?BJ-sH2cmyvp^Xk28oJ$d_~K%Gdtl z#0HYiBHCQZo{R~O{4?c!miZzw*@vS1{%n{a9uC-NV#k9h+J9qjGy7#qmZ$4ev}Bp< zttvI_7NQ$xw|3_4aFhBsW;HysH<1v}o1JCCj<$8YiB`=PX&aELJ{ILW$%dPx%#k>uvBz?z(8JbwEVqvFF)Y#)2`tvTA&!W=nWV9t?1+Z> zWe(VTlQ;}=MglP;r{F0fpCs?o+uwAy59!^NS8pN~(jCQxu^dFV102ims!Jnxv?zv6 zs6>?8oUn4nf%y)O%?RykxXN|J8CL~Tz>yv@eNMfs$o9J(Hd#+e;P>gl>`=0Mrm=XWm)&s1$b58<)u0InJsNjc{9Q&282jy^k+g!T0;fIf2!_M>@^ia}yddqWuX1vYw7Lb81Ph+$HO#QdyN; zDqPzM&tIimK?^lM1b3ILHP2Y8qvdRaP>HdqcF>Y=>Vj^oqqAFe;-bNbWSThVqA9zY zx*!Ub6hR+SGnE$xvF3%DWnvR(R)?D-tUn1Q#k(LP-gmtrh|nu{N{}8B5k0!9KKoU2 zcGSdKnHw%GE*9lv)r6!$4gNa&tS_HpuTrJ{gzZ6ZO2}lQ2sOVWB%nxbVfsiuVnkc< zlpq9Nn9&`xNIbV>R$%~2jLjlwMt!=UI-%duuB_NF?8>sBmbP44k}qC6fK$#Nb##*P zE5sR3L6x~*;x1-c;Vxzg>dyMPK7{gJ4IbHvkDPG?LXSmJ=C4z!6&DMk`Cv;@*1UJv zvy_>bAtFN%5nvQc|D$G{Vo(7gj+3cCqv5SdM6qsoi70lHj5mhdl12{e&Z|F?%T#tPAR+8_Jk)2Isut)H>T%cewreQtOwd z)`@9}k~E>9=MyYg!G4KsQPd}kwuLM$?4e2~bO#a2ke()Z$y-Ag&)zL&wOUeUC12a; zf&?zresoylRZ$}41W{+w!Re;DJybjww&BGLtg|E{))ad)GL0wNKxhbW0~-;m)^n5` z)80NuTD7+uZak4k6k%qd3`P`^V2Ny7i-n*V z$jF97sW}ZC>DmYg3evp&es{bwKsj-Ied6`5mSkpXF ziEHSsLi`2mTh+WIZb%y@kwwm^#8CCX(PveC-mnebQA`uO*$Jdt&zC-z)Tx^=`#9te zl@-qUP?l6HK>VLNX=5Di!d*&G-FgZNm(6(Cw5n#;iXCM05DFYm_l`I&wQ#~oRxC;iQ2H5>am9FtI_X*AlIdGbDu zcZ4hFMVXQ*iDLwsUG}lM72!L?fCz(TvWaQ?a%=;Z6+GnDr zyO_n*AyK%kU2K}aV5N#Lyvp2>t-bpoMnp4!`RuZ$g(i|+!?=x z{01&#aop&SeOB?`B=V2)>r zaGQ8a-x~AYB@1A$KrhTzISZD9P?#mad{`ZcQWtw0-B&Qwy(sk z#8&BXM||TP-5x4D35?hz*GH-9-4+?No}n5ZNX0$YdQ08cV!N|VvK&y!-W=Ac3#Z5* z$y|_5hhY>pMY5BhvGHBeX~Q398>Mc)@i{C=@`+Br`rX~SXa{TO1gV5N z8Um7zlcZTF#U&)krkpeXTy_A*w%JqElc^TOac&U4#Ul1Wb!ynZs>}b4dwuvQ&vCfH z&xT{ENI;P7<|0T$6|#9RzLP2!1G~1&5~I=tPbbr$p_gRr+HjqK%>RTX6t!k{ujK%X z3m`Jsa}7Zi2}b(eI>N*fYI3TLiwZdP5DP3DQ;Vec4}_xLlUEM`Sl#`Mc9q$pmThGT z$v||^B>^J_&r0}KZ8Vklo<*b7Zb&E1-DYC^4rj_K71QQ#diT+7B54+~=~wUZkX9*H zjV7b}>rdTyi7&VV&g++t^l}&j!KM+11(L8SLP(LioIMzi-ZqN6yArSv4sgQ7f>M)a z&czHenNPxg;TchPsf{d)=~G)35lGRVG203etN65)WGrnp>|g!nL!=WX-I^4I+2x&W2!_r#^Yy6rAi#c^LeZoM4Gj8pjWy;$c|LC$tAk3__1 zoT1@p{4JWmcwGMC8jzr_Dqi|cs)vxMli{957mpQK)8d#M@bNpivP%<=Q({qWYgWx8 zdCqHB@z3)k9OLh|q#iw>ibO{rc(NEjg^i9hE#mJR2x0t)( zpD}y;efprTpy;V2PFxo2g~KZax&I{_>TKwEk)*&o<~O6k-BgHlwwnrBC3l|5*+C(n zsdXr_%j;36hS?Sj6UQe>k78EBD~ZL#P!X={mL6b*zIa@IC+(i7MArpDe5ntiX z(?Kp+ktmsHRxYuKMaZ1~aiMx=+&t$Ybk~Zww5lMw@*Dc`Cfq|@24nWnBtdkw?%n-P zv4g|*pn3c*!_OPUmipCdl1!FXZURoNAC%-JAMs{j8e*!Of0y$(Us4_|G$4{`5@o6` zW9VhF(=sriToD~yi1{1l;dtLhNkHVQvQTIo!TX_@)?~NYZkNwpL9!@zbVO;?NkNRT zx*YiIh(@vW?iJzxkdMeeLAW|2KO{zT<$0BQ^KMe-)=esu#jVuBE*g&Tfm#de`zd!m zh#l(}Q5rTO5{(3J5@-2E4~v*G5gzb)R`Vsz%C$bHu631#a@UlIa1GP=eIPX+!_(?= zculvu>disUqha+*__|Mmwiz!F8hjHqf9g^JTI-EaIS`FSp0aSW!c z-X(rd?3y=a2%iKLcXlQFuip5LX-@Ux9F_Ui)X18Od_n=bgCY{Z{0d13krVXwrsd>b zmt;+6)8yRAXBFI)P3$*@GE->9n+=SMY!g-6SZN6LxUs1E(V0;kE{zV5iBjn{^K-&s zQ7+aKg(OpMw1`{?5B-U~j@FbNU1?53XgaI)q$PXEt5fOhSetqUnzP{&Ej>@@tczPN z&Hy%k+6{?AX!Tn<7H}iREe9p)TM5k!udAeYPREyvrr-4poZdEWkyIQjNfKZOQP;XL zMPC?CMT76CW8OA;8VeN?4=Sc&M(?Ge<7Anf{+RQ8?=>@>p^glrpF$tnSa<+eatdVf zmTXA7cvvjK%CEW5etbXIrEE3)m57B&TW@_r87A{1=^%>BQ-bbzj}glOtCoF4CJEomw!7E`x$T*_~$2RlvFHUK}s_SJ>vPT(amUSST-?DqVG* zB1uZYJ0K%KJ106lOcjv|>g#)xsy@wSw)!0n%o-V^l5hpRkA(V#Z3p+FP(N6BWl|$+ zB(_QIV&zVN`sH##@-}jGBw5GDIp$ilNO)2FVlpd~@9LOaOnQxa%ON24rJYsp6ppU9 zrb&3}R|8InN9W5uXivDl#0ZwXaJ%hMTmaK_-lK9}fN(;O*J$dpik4+7OTs(eo?YIb zUq74FFsyR9TrhswgB-eHC)qZY**@?|p0e_8QYM*Cr4&`|G73Gz>ueCW7F>In&JTyN zE0@{jCqU*%g|InkIGdO^{&vX;F6weUu|g%GI3-p&upkmuc2Oso(_~nO=Clmo1iOpq zr*dvTaMB2Ow2#)9=RLn6#5E5wBMahqfUb~1n9LG-`)@+)BApwu3m4iJX`O>4MvOZR zQulVXw<6ASfhEbBw`kRh?yGwjC&i(wvwYbL1Qu$;R)uC zbBr#W8lQHF4v^m^j*?86QtNZ<6V=CY!}V&Q!gPJUP`Ht#-R_jTtVoBF+xhEB9?K8y z9Z`L#enKnE*>SNwDo;6e_L}G^A;sPe*))d;&04BcH?kcXNg@j;s*57RTGLkiMIAcn znYslm^^bDzqU2vOr3JnWk^ypytKRP0U{8tyd=3R!KbcN(*`@4yoU4BIz5RpQk%>Z@ z)*bG!06;7=EtC;X{QA&SxJ@T(Eo4!4DaodUoM_S;t?On?Cr5mGXGO~d%2a7}e;x{{ z~1knZPBVpOQU-ziK@Sn9%CFIjBIGaucMtY<$r zB}Y)Plhvskan3?|l;N7+C|Qm54x7+})7%;g6p7(2Q|NhxzZhQq3F}7Wt7OKY12#5s zKUIL%zZx=OWI4SCFqUGCfMFHGD>P+6{FK>oWj{_VMLi= EDISON_OOBE_PRESS_TIMEOUT && event_already_fired == 0) + { + event_already_fired = 1; + printf("Edison PWR button was pressed more than 2s\n"); + fflush(stdout); + system(argv[1]); + } continue; + } len = read(fd, &event, sizeof(event)); if (len < 0) { @@ -99,7 +119,7 @@ int main(int argc, char **argv) break; } - /* ignore non KEY event, and non RM button events */ + /* ignore non KEY event, and non PWR button events */ if (event.type != EV_KEY || event.code != KEY_POWER) continue; @@ -116,6 +136,7 @@ int main(int argc, char **argv) { case 1: /* Regular press */ assert(time_at_last_press==0); + assert(event_already_fired==0); time_at_last_press = tv.tv_sec; break; case 2: /* Auto repeat press */ @@ -126,14 +147,14 @@ int main(int argc, char **argv) } break; case 0: /* Release */ - if (tv.tv_sec - time_at_last_press >= EDISON_OOBE_PRESS_TIMEOUT) + if (event_already_fired != 0) { - time_at_last_press = 0; - printf("Edison PWR button was pressed more than 2s, starting OOBE service..\n"); + printf("Edison PWR button was pressed more than 2s and released\n"); fflush(stdout); - system(CALLED_COMMAND_ON_TIMEOUT); + system(argv[2]); } time_at_last_press = 0; + event_already_fired = 0; break; default: printf("Warning: unhandled PWR button event value: %u\n", event.value); diff --git a/device-software/meta-edison-distro/recipes-support/pwr-button-handler/files/pwr-button-handler.service b/device-software/meta-edison-distro/recipes-support/pwr-button-handler/files/pwr-button-handler.service index 48e9266..a53c10b 100644 --- a/device-software/meta-edison-distro/recipes-support/pwr-button-handler/files/pwr-button-handler.service +++ b/device-software/meta-edison-distro/recipes-support/pwr-button-handler/files/pwr-button-handler.service @@ -2,7 +2,7 @@ Description=Edison PWR button handler [Service] -ExecStart=/usr/bin/pwr_button_handler +ExecStart=/usr/bin/pwr_button_handler "/bin/systemctl start blink-led" "sh -c \"/bin/systemctl stop blink-led && /usr/bin/configure_edison --enableOneTimeSetup\"" Restart=on-failure [Install] diff --git a/device-software/meta-edison-middleware/recipes-connectivity/c-ares/c-ares_1.10.0.bb b/device-software/meta-edison-middleware/recipes-connectivity/c-ares/c-ares_1.10.0.bb new file mode 100644 index 0000000..0d1ef9d --- /dev/null +++ b/device-software/meta-edison-middleware/recipes-connectivity/c-ares/c-ares_1.10.0.bb @@ -0,0 +1,13 @@ +DESCRIPTION = "C library for asynchronous DNS requests (including name resolves)" +HOMEPAGE = "http://c-ares.haxx.se/" +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://README;beginline=17;endline=23;md5=d08205a43bc63c12cf394ac1d2cce7c3" + +PR = "r0" + +SRC_URI = "http://c-ares.haxx.se/download/c-ares-${PV}.tar.gz" + +SRC_URI[md5sum] = "1196067641411a75d3cbebe074fd36d8" +SRC_URI[sha256sum] = "3d701674615d1158e56a59aaede7891f2dde3da0f46a6d3c684e0ae70f52d3db" + +inherit autotools diff --git a/device-software/meta-edison-middleware/recipes-connectivity/mdns/files/build.patch b/device-software/meta-edison-middleware/recipes-connectivity/mdns/files/build.patch new file mode 100644 index 0000000..a1a2c89 --- /dev/null +++ b/device-software/meta-edison-middleware/recipes-connectivity/mdns/files/build.patch @@ -0,0 +1,191 @@ +--- a/Clients/Makefile ++++ b/Clients/Makefile +@@ -30,9 +30,17 @@ TARGETS = build/dns-sd build/dns-sd64 + LIBS = + else + TARGETS = build/dns-sd ++# Set up diverging paths for debug vs. prod builds ++DEBUG?=1 ++ifeq ($(DEBUG),1) ++LIBS = -L../mDNSPosix/build/debug/ -ldns_sd ++else + LIBS = -L../mDNSPosix/build/prod/ -ldns_sd + endif + ++ ++endif ++ + all: $(TARGETS) + + clean: +@@ -42,10 +50,10 @@ build: + mkdir build + + build/dns-sd: build dns-sd.c ClientCommon.c +- cc $(filter %.c %.o, $+) $(LIBS) -I../mDNSShared -Wall -o $@ ++ $(CC) $(filter %.c %.o, $+) $(LIBS) -I../mDNSShared -Wall -o $@ + + build/dns-sd64: build dns-sd.c ClientCommon.c +- cc $(filter %.c %.o, $+) $(LIBS) -I../mDNSShared -Wall -o $@ -m64 ++ $(CC) $(filter %.c %.o, $+) $(LIBS) -I../mDNSShared -Wall -o $@ -m64 + + # Note, we can make a 'fat' version of dns-sd using 'lipo', as shown below, but we + # don't, because we don't want or need a 'fat' version of dns-sd, because it will +--- a/mDNSPosix/Makefile ++++ b/mDNSPosix/Makefile +@@ -50,6 +50,7 @@ + + LIBVERS = 1 + ++POSIXDIR = ../mDNSPosix + COREDIR = ../mDNSCore + SHAREDDIR ?= ../mDNSShared + JDK = /usr/jdk +@@ -57,11 +58,11 @@ JDK = /usr/jdk + CC = @cc + BISON = @bison + FLEX = @flex +-LD = ld -shared ++LD =@LD + CP = cp + RM = rm + LN = ln -s -f +-CFLAGS_COMMON = -I$(COREDIR) -I$(SHAREDDIR) -I$(OBJDIR) -fwrapv -W -Wall -DPID_FILE=\"/var/run/mdnsd.pid\" -DMDNS_UDS_SERVERPATH=\"/var/run/mdnsd\" ++CFLAGS_COMMON = -I$(POSIXDIR) -I$(COREDIR) -I$(SHAREDDIR) -I$(OBJDIR) -fwrapv -W -Wall -DPID_FILE=\"/var/run/mdnsd.pid\" -DMDNS_UDS_SERVERPATH=\"/var/run/mdnsd\" + CFLAGS_PTHREAD = + LINKOPTS = + LINKOPTS_PTHREAD = -lpthread +@@ -69,7 +70,7 @@ LDSUFFIX = so + JAVACFLAGS_OS = -fPIC -shared -ldns_sd + + # Set up diverging paths for debug vs. prod builds +-DEBUG=0 ++DEBUG?=1 + ifeq ($(DEBUG),1) + CFLAGS_DEBUG = -g -DMDNS_DEBUGMSGS=2 + OBJDIR = objects/debug +@@ -103,6 +104,7 @@ else + ifeq ($(findstring linux,$(os)),linux) + CFLAGS_OS = -D_GNU_SOURCE -DHAVE_IPV6 -DNOT_HAVE_SA_LEN -DUSES_NETLINK -DHAVE_LINUX -DTARGET_OS_LINUX -fno-strict-aliasing + LD = gcc -shared ++LINKOPTS = --hash-style=gnu + FLEXFLAGS_OS = -l + JAVACFLAGS_OS += -I$(JDK)/include/linux + +@@ -210,7 +212,7 @@ endif + endif + endif + +-CFLAGS = $(CFLAGS_COMMON) $(CFLAGS_OS) $(CFLAGS_DEBUG) ++CFLAGS_BUILD = $(CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_OS) $(CFLAGS_DEBUG) + + ############################################################################# + +@@ -246,8 +248,7 @@ Daemon: setup $(BUILDDIR)/mdnsd + @echo "Responder daemon done" + + $(BUILDDIR)/mdnsd: $(DAEMONOBJS) +- $(CC) -o $@ $+ $(LINKOPTS) +- @$(STRIP) $@ ++ $(CC) -o $@ $+ + + # libdns_sd target builds the client library + libdns_sd: setup $(BUILDDIR)/libdns_sd.$(LDSUFFIX) +@@ -256,22 +257,18 @@ libdns_sd: setup $(BUILDDIR)/libdns_sd.$ + CLIENTLIBOBJS = $(OBJDIR)/dnssd_clientlib.c.so.o $(OBJDIR)/dnssd_clientstub.c.so.o $(OBJDIR)/dnssd_ipc.c.so.o + + $(BUILDDIR)/libdns_sd.$(LDSUFFIX): $(CLIENTLIBOBJS) +- @$(LD) $(LINKOPTS) -o $@ $+ +- @$(STRIP) $@ ++ $(LD) -shared $(LINKOPTS) -o $@ $+ + +-Clients: setup libdns_sd ../Clients/build/dns-sd ++Clients: setup libdns_sd ++ @$(MAKE) -C ../Clients DEBUG=${DEBUG} + @echo "Clients done" + +-../Clients/build/dns-sd: +- @$(MAKE) -C ../Clients +- + # nss_mdns target builds the Name Service Switch module + nss_mdns: setup $(BUILDDIR)/$(NSSLIBFILE) + @echo "Name Service Switch module done" + + $(BUILDDIR)/$(NSSLIBFILE): $(CLIENTLIBOBJS) $(OBJDIR)/nss_mdns.c.so.o +- @$(LD) $(LINKOPTS) -o $@ $+ +- @$(STRIP) $@ ++ $(LD) -shared $(LINKOPTS) -o $@ $+ + + ############################################################################# + +@@ -469,55 +466,55 @@ dnsextd: setup $(BUILDDIR)/dnsextd + @echo "dnsextd done" + + $(BUILDDIR)/mDNSClientPosix: $(APPOBJ) $(OBJDIR)/Client.c.o +- $(CC) $+ -o $@ $(LINKOPTS) ++ $(CC) $+ -o $@ + + $(BUILDDIR)/mDNSResponderPosix: $(COMMONOBJ) $(OBJDIR)/Responder.c.o +- $(CC) $+ -o $@ $(LINKOPTS) ++ $(CC) $+ -o $@ + + $(BUILDDIR)/mDNSProxyResponderPosix: $(COMMONOBJ) $(OBJDIR)/ProxyResponder.c.o +- $(CC) $+ -o $@ $(LINKOPTS) ++ $(CC) $+ -o $@ + + $(BUILDDIR)/mDNSIdentify: $(SPECIALOBJ) $(OBJDIR)/Identify.c.o +- $(CC) $+ -o $@ $(LINKOPTS) ++ $(CC) $+ -o $@ + + $(OBJDIR)/Identify.c.o: $(COREDIR)/mDNS.c # Note: Identify.c textually imports mDNS.c + + $(BUILDDIR)/mDNSNetMonitor: $(SPECIALOBJ) $(OBJDIR)/NetMonitor.c.o +- $(CC) $+ -o $@ $(LINKOPTS) ++ $(CC) $+ -o $@ + + $(OBJDIR)/NetMonitor.c.o: $(COREDIR)/mDNS.c # Note: NetMonitor.c textually imports mDNS.c + + $(BUILDDIR)/dnsextd: $(DNSEXTDOBJ) $(OBJDIR)/dnsextd.c.threadsafe.o +- $(CC) $+ -o $@ $(LINKOPTS) $(LINKOPTS_PTHREAD) ++ $(CC) $+ -o $@ $(LINKOPTS_PTHREAD) + + ############################################################################# + + # Implicit rules + $(OBJDIR)/%.c.o: %.c +- $(CC) $(CFLAGS) -c -o $@ $< ++ $(CC) $(CFLAGS_BUILD) -c -o $@ $< + + $(OBJDIR)/%.c.o: $(COREDIR)/%.c +- $(CC) $(CFLAGS) -c -o $@ $< ++ $(CC) $(CFLAGS_BUILD) -c -o $@ $< + + $(OBJDIR)/%.c.o: $(SHAREDDIR)/%.c +- $(CC) $(CFLAGS) -c -o $@ $< ++ $(CC) $(CFLAGS_BUILD) -c -o $@ $< + + $(OBJDIR)/%.c.threadsafe.o: %.c +- $(CC) $(CFLAGS) $(CFLAGS_PTHREAD) -D_REENTRANT -c -o $@ $< ++ $(CC) $(CFLAGS_BUILD) $(CFLAGS_PTHREAD) -D_REENTRANT -c -o $@ $< + + $(OBJDIR)/%.c.threadsafe.o: $(SHAREDDIR)/%.c +- $(CC) $(CFLAGS) $(CFLAGS_PTHREAD) -D_REENTRANT -c -o $@ $< ++ $(CC) $(CFLAGS_BUILD) $(CFLAGS_PTHREAD) -D_REENTRANT -c -o $@ $< + + $(OBJDIR)/%.c.so.o: %.c +- $(CC) $(CFLAGS) -c -fPIC -o $@ $< ++ $(CC) $(CFLAGS_BUILD) -c -fPIC -o $@ $< + + $(OBJDIR)/%.c.so.o: $(SHAREDDIR)/%.c +- $(CC) $(CFLAGS) -c -fPIC -o $@ $< ++ $(CC) $(CFLAGS_BUILD) -c -fPIC -o $@ $< + + $(OBJDIR)/%.y.o: $(SHAREDDIR)/%.y + $(BISON) -o $(OBJDIR)/$*.c -d $< +- $(CC) $(CFLAGS) -c -o $@ $(OBJDIR)/$*.c ++ $(CC) $(CFLAGS_BUILD) -c -o $@ $(OBJDIR)/$*.c + + $(OBJDIR)/%.l.o: $(SHAREDDIR)/%.l + $(FLEX) $(FLEXFLAGS_OS) -i -o$(OBJDIR)/$*.l.c $< +- $(CC) $(CFLAGS) -Wno-error -c -o $@ $(OBJDIR)/$*.l.c ++ $(CC) $(CFLAGS_BUILD) -Wno-error -c -o $@ $(OBJDIR)/$*.l.c diff --git a/device-software/meta-edison-middleware/recipes-connectivity/mdns/files/mdns.service b/device-software/meta-edison-middleware/recipes-connectivity/mdns/files/mdns.service index 5732e78..531d142 100644 --- a/device-software/meta-edison-middleware/recipes-connectivity/mdns/files/mdns.service +++ b/device-software/meta-edison-middleware/recipes-connectivity/mdns/files/mdns.service @@ -3,7 +3,11 @@ Description=Zero-configuration networking After=network.target [Service] +Type=forking +ExecStartPre=/bin/rm -f /var/run/mdnsd.pid ExecStart=/usr/sbin/mdnsd +ExecReload=/bin/kill -HUP $MAINPID +PIDFile=/var/run/mdnsd.pid Restart=always RestartSec=10s diff --git a/device-software/meta-edison-middleware/recipes-connectivity/mdns/mdns_544.bb b/device-software/meta-edison-middleware/recipes-connectivity/mdns/mdns_544.bb index e1633b1..1d3f0bd 100644 --- a/device-software/meta-edison-middleware/recipes-connectivity/mdns/mdns_544.bb +++ b/device-software/meta-edison-middleware/recipes-connectivity/mdns/mdns_544.bb @@ -1,28 +1,33 @@ DESCRIPTION = "Bonjour, also known as zero-configuration networking, enables automatic discovery of computers, devices, and services on IP networks." HOMEPAGE = "http://developer.apple.com/networking/bonjour/" LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=31c50371921e0fb731003bbc665f29bf" -S = "${EDISONREPO_TOP_DIR}/mw/mdns" +PR = "r1" -SRC_URI = "file://mdns.service" - -LIC_FILES_CHKSUM = " \ - file://LICENSE;md5=31c50371921e0fb731003bbc665f29bf \ +SRC_URI = "http://opensource.apple.com/tarballs/mDNSResponder/mDNSResponder-${PV}.tar.gz \ + file://build.patch \ + file://mdns.service \ " +SRC_URI[md5sum] = "39142ab70bd82a096801ce346f86cbab" +SRC_URI[sha256sum] = "c6ad1d53c28d28c0e3689bdf5efd9ce6f5c4c3692e8ad76e5eeb4d0c248929ac" + PARALLEL_MAKE = "" +S = "${WORKDIR}/mDNSResponder-544" + do_compile() { cd mDNSPosix - oe_runmake os=linux + oe_runmake os=linux DEBUG=0 } do_install () { install -d ${D}${sbindir} - install -m 0755 mDNSPosix/build/debug/mdnsd ${D}${sbindir} + install -m 0755 mDNSPosix/build/prod/mdnsd ${D}${sbindir} install -d ${D}${libdir} - cp mDNSPosix/build/debug/libdns_sd.so ${D}${libdir}/libdns_sd.so.1 + cp mDNSPosix/build/prod/libdns_sd.so ${D}${libdir}/libdns_sd.so.1 chmod 0644 ${D}${libdir}/libdns_sd.so.1 ln -s libdns_sd.so.1 ${D}${libdir}/libdns_sd.so @@ -36,7 +41,7 @@ do_install () { install -m 0755 Clients/build/dns-sd ${D}${bindir} install -d ${D}${libdir} - oe_libinstall -C mDNSPosix/build/debug -so libnss_mdns-0.2 ${D}${libdir} + oe_libinstall -C mDNSPosix/build/prod -so libnss_mdns-0.2 ${D}${libdir} ln -s libnss_mdns-0.2.so ${D}${libdir}/libnss_mdns.so.2 install -d ${D}${sysconfdir} diff --git a/device-software/meta-edison-middleware/recipes-connectivity/mosquitto/files/build.patch b/device-software/meta-edison-middleware/recipes-connectivity/mosquitto/files/build.patch new file mode 100644 index 0000000..48c0653 --- /dev/null +++ b/device-software/meta-edison-middleware/recipes-connectivity/mosquitto/files/build.patch @@ -0,0 +1,71 @@ +Index: mosquitto-1.3.4/client/Makefile +=================================================================== +--- mosquitto-1.3.4.orig/client/Makefile ++++ mosquitto-1.3.4/client/Makefile +@@ -21,8 +21,8 @@ sub_client.o : sub_client.c ../lib/libmo + + install : all + $(INSTALL) -d ${DESTDIR}$(prefix)/bin +- $(INSTALL) -s --strip-program=$(STRIP) mosquitto_pub ${DESTDIR}${prefix}/bin/mosquitto_pub +- $(INSTALL) -s --strip-program=$(STRIP) mosquitto_sub ${DESTDIR}${prefix}/bin/mosquitto_sub ++ $(INSTALL) mosquitto_pub ${DESTDIR}${prefix}/bin/mosquitto_pub ++ $(INSTALL) mosquitto_sub ${DESTDIR}${prefix}/bin/mosquitto_sub + + uninstall : + -rm -f ${DESTDIR}${prefix}/bin/mosquitto_pub +Index: mosquitto-1.3.4/config.mk +=================================================================== +--- mosquitto-1.3.4.orig/config.mk ++++ mosquitto-1.3.4/config.mk +@@ -213,7 +213,7 @@ endif + + + INSTALL?=install +-prefix=/usr/local ++prefix=/usr + mandir=${prefix}/share/man + localedir=${prefix}/share/locale + STRIP?=strip +Index: mosquitto-1.3.4/lib/Makefile +=================================================================== +--- mosquitto-1.3.4.orig/lib/Makefile ++++ mosquitto-1.3.4/lib/Makefile +@@ -25,7 +25,7 @@ all : libmosquitto.so.${SOVERSION} libmo + + install : all + $(INSTALL) -d ${DESTDIR}$(prefix)/lib${LIB_SUFFIX}/ +- $(INSTALL) -s --strip-program=$(STRIP) libmosquitto.so.${SOVERSION} ${DESTDIR}${prefix}/lib${LIB_SUFFIX}/libmosquitto.so.${SOVERSION} ++ $(INSTALL) libmosquitto.so.${SOVERSION} ${DESTDIR}${prefix}/lib${LIB_SUFFIX}/libmosquitto.so.${SOVERSION} + ln -sf libmosquitto.so.${SOVERSION} ${DESTDIR}${prefix}/lib${LIB_SUFFIX}/libmosquitto.so + $(INSTALL) -d ${DESTDIR}${prefix}/include/ + $(INSTALL) mosquitto.h ${DESTDIR}${prefix}/include/mosquitto.h +Index: mosquitto-1.3.4/lib/cpp/Makefile +=================================================================== +--- mosquitto-1.3.4.orig/lib/cpp/Makefile ++++ mosquitto-1.3.4/lib/cpp/Makefile +@@ -10,7 +10,7 @@ all : libmosquittopp.so.${SOVERSION} + + install : all + $(INSTALL) -d ${DESTDIR}$(prefix)/lib${LIB_SUFFIX}/ +- $(INSTALL) -s --strip-program=$(STRIP) libmosquittopp.so.${SOVERSION} ${DESTDIR}${prefix}/lib${LIB_SUFFIX}/libmosquittopp.so.${SOVERSION} ++ $(INSTALL) libmosquittopp.so.${SOVERSION} ${DESTDIR}${prefix}/lib${LIB_SUFFIX}/libmosquittopp.so.${SOVERSION} + ln -sf libmosquittopp.so.${SOVERSION} ${DESTDIR}${prefix}/lib${LIB_SUFFIX}/libmosquittopp.so + $(INSTALL) -d ${DESTDIR}${prefix}/include/ + $(INSTALL) mosquittopp.h ${DESTDIR}${prefix}/include/mosquittopp.h +Index: mosquitto-1.3.4/src/Makefile +=================================================================== +--- mosquitto-1.3.4.orig/src/Makefile ++++ mosquitto-1.3.4/src/Makefile +@@ -100,10 +100,10 @@ mosquitto_passwd.o : mosquitto_passwd.c + + install : all + $(INSTALL) -d ${DESTDIR}$(prefix)/sbin +- $(INSTALL) -s --strip-program=$(STRIP) mosquitto ${DESTDIR}${prefix}/sbin/mosquitto ++ $(INSTALL) mosquitto ${DESTDIR}${prefix}/sbin/mosquitto + $(INSTALL) mosquitto_plugin.h ${DESTDIR}${prefix}/include/mosquitto_plugin.h + ifeq ($(WITH_TLS),yes) +- $(INSTALL) -s --strip-program=$(STRIP) mosquitto_passwd ${DESTDIR}${prefix}/bin/mosquitto_passwd ++ $(INSTALL) mosquitto_passwd ${DESTDIR}${prefix}/bin/mosquitto_passwd + endif + + uninstall : diff --git a/device-software/meta-edison-middleware/recipes-connectivity/mosquitto/files/mosquitto.service b/device-software/meta-edison-middleware/recipes-connectivity/mosquitto/files/mosquitto.service new file mode 100644 index 0000000..25f68fa --- /dev/null +++ b/device-software/meta-edison-middleware/recipes-connectivity/mosquitto/files/mosquitto.service @@ -0,0 +1,15 @@ +[Unit] +Description=Mosquitto - lightweight server implementation of the MQTT and MQTT-SN protocols +ConditionPathExists=/etc/mosquitto/mosquitto.conf +After=network.target + +[Service] +Type=simple +ExecStartPre=/bin/rm -f /var/run/mosquitto.pid +ExecStart=/usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf +ExecReload=/bin/kill -HUP $MAINPID +PIDFile=/var/run/mosquitto.pid +Restart=on-failure + +[Install] +WantedBy=multi-user.target diff --git a/device-software/meta-edison-middleware/recipes-connectivity/mosquitto/mosquitto_1.3.4.bb b/device-software/meta-edison-middleware/recipes-connectivity/mosquitto/mosquitto_1.3.4.bb new file mode 100644 index 0000000..84c3307 --- /dev/null +++ b/device-software/meta-edison-middleware/recipes-connectivity/mosquitto/mosquitto_1.3.4.bb @@ -0,0 +1,55 @@ +SUMMARY = "Open source MQTT v3.1 implemention" +DESCRIPTION = "Mosquitto is an open source (BSD licensed) message broker that implements the MQ Telemetry Transport protocol version 3.1. MQTT provides a lightweight method of carrying out messaging using a publish/subscribe model. " +HOMEPAGE = "http://mosquitto.org/" +SECTION = "console/network" +LICENSE = "BSD" +LIC_FILES_CHKSUM = "file://LICENSE.txt;md5=89aa5ea5f32e4260d84c5d185ee3add4" + +DEPENDS = "openssl c-ares" + +PR = "r0" + +SRC_URI = "http://mosquitto.org/files/source/mosquitto-${PV}.tar.gz \ + file://build.patch \ + file://mosquitto.service \ +" + +SRC_URI[md5sum] = "9d729849efd74c6e3eee17a4a002e1e9" +SRC_URI[sha256sum] = "0a3982d6b875a458909c8828731da04772035468700fa7eb2f0885f4bd6d0dbc" + +inherit autotools + +do_install_append () { + install -d ${D}${libbir} + install -m 0644 lib/libmosquitto.a ${D}${libdir}/ + + install -d ${D}${systemd_unitdir}/system/ + install -m 0644 ${WORKDIR}/mosquitto.service ${D}${systemd_unitdir}/system/ + + cp ${D}${sysconfdir}/mosquitto/mosquitto.conf.example ${D}${sysconfdir}/mosquitto/mosquitto.conf + sed 's/#user mosquitto/user root/' -i ${D}${sysconfdir}/mosquitto/mosquitto.conf +} + +PACKAGES += "libmosquitto1 libmosquittopp1 ${PN}-clients ${PN}-python" + +FILES_${PN} = "${sbindir}/mosquitto \ + ${bindir}/mosquitto_passwd \ + ${sysconfdir} \ + ${systemd_unitdir}/system/mosquitto.service \ +" + +FILES_libmosquitto1 = "${libdir}/libmosquitto.so.1" + +FILES_libmosquittopp1 = "${libdir}/libmosquittopp.so.1" + +FILES_${PN}-clients = "${bindir}/mosquitto_pub \ + ${bindir}/mosquitto_sub \ +" + +FILES_${PN}-staticdev += "${libdir}/libmosquitto.a" + +FILES_${PN}-python = "/usr/lib/python2.7/site-packages" + +inherit systemd + +SYSTEMD_SERVICE_${PN} = "mosquitto.service" diff --git a/device-software/meta-edison-middleware/recipes-connectivity/paho-mqtt/files/makefile.patch b/device-software/meta-edison-middleware/recipes-connectivity/paho-mqtt/files/makefile.patch new file mode 100644 index 0000000..4d1e25b --- /dev/null +++ b/device-software/meta-edison-middleware/recipes-connectivity/paho-mqtt/files/makefile.patch @@ -0,0 +1,19 @@ +--- + Makefile | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/Makefile b/Makefile +index 33bd2d0..84262bd 100644 +--- a/Makefile ++++ b/Makefile +@@ -115,7 +115,8 @@ LDFLAGS_AS = -shared -Wl,-soname,lib${MQTTLIB_AS}.so.${MAJOR_VERSION} -lpthread + + all: build + +-build: | mkdir ${MQTTLIB_C_TARGET} ${MQTTLIB_CS_TARGET} ${MQTTLIB_A_TARGET} ${MQTTLIB_AS_TARGET} ${MQTTVERSION_TARGET} ${SYNC_SAMPLES} ${ASYNC_SAMPLES} ${SYNC_TESTS} ${SYNC_SSL_TESTS} ${ASYNC_TESTS} ${ASYNC_SSL_TESTS} ++build: | mkdir ${MQTTLIB_C_TARGET} ${MQTTLIB_CS_TARGET} ${MQTTLIB_A_TARGET} ${MQTTLIB_AS_TARGET} ++#${MQTTVERSION_TARGET} ${SYNC_SAMPLES} ${ASYNC_SAMPLES} ${SYNC_TESTS} ${SYNC_SSL_TESTS} ${ASYNC_TESTS} ${ASYNC_SSL_TESTS} + + clean: + rm -rf ${blddir}/* +-- diff --git a/device-software/meta-edison-middleware/recipes-connectivity/paho-mqtt/paho-mqtt_3.1.bb b/device-software/meta-edison-middleware/recipes-connectivity/paho-mqtt/paho-mqtt_3.1.bb index 7f3bd2f..37cab21 100644 --- a/device-software/meta-edison-middleware/recipes-connectivity/paho-mqtt/paho-mqtt_3.1.bb +++ b/device-software/meta-edison-middleware/recipes-connectivity/paho-mqtt/paho-mqtt_3.1.bb @@ -1,13 +1,24 @@ DESCRIPTION = "Paho MQTT - user libraries for the MQTT and MQTT-SN protocols" +DESCRIPTION = "Client implementation of open and standard messaging protocols for Machine-to-Machine (M2M) and Internet of Things (IoT)." +HOMEPAGE = "http://www.eclipse.org/paho/" +SECTION = "console/network" LICENSE = "EPL-1.0 | EDL-1.0" -S = "${EDISONREPO_TOP_DIR}/mw/mqtt" - LIC_FILES_CHKSUM = " \ - file://edl-v10;md5=3adfcc70f5aeb7a44f3f9b495aa1fbf3 \ - file://epl-v10;md5=659c8e92a40b6df1d9e3dccf5ae45a08 \ + file://edl-v10;md5=3adfcc70f5aeb7a44f3f9b495aa1fbf3 \ + file://epl-v10;md5=659c8e92a40b6df1d9e3dccf5ae45a08 \ +" + +PR = "r1" + +SRC_URI = "git://git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.c.git;protocol=http \ + file://makefile.patch \ " +SRCREV = "93a064dbe9fd2fc84b8bb2701e10d2de8004a11c" + +S = "${WORKDIR}/git" + do_compile() { oe_runmake } @@ -25,4 +36,4 @@ do_install() { } DEPENDS = "openssl" - +RDEPENDS_${PN} = "openssl" diff --git a/device-software/meta-edison-middleware/recipes-connectivity/sshpass/sshpass.inc b/device-software/meta-edison-middleware/recipes-connectivity/sshpass/sshpass.inc new file mode 100644 index 0000000..b19ae43 --- /dev/null +++ b/device-software/meta-edison-middleware/recipes-connectivity/sshpass/sshpass.inc @@ -0,0 +1,10 @@ +DESCRIPTION = "Non-interactive ssh password auth" +HOMEPAGE = "http://sshpass.sourceforge.net/" +SECTION = "console/network" +LICENSE = "GPLv2" + +SRC_URI = "${SOURCEFORGE_MIRROR}/sshpass/sshpass-${PV}.tar.gz" + +INC_PR = "r0" + +inherit autotools diff --git a/device-software/meta-edison-middleware/recipes-connectivity/sshpass/sshpass_1.05.bb b/device-software/meta-edison-middleware/recipes-connectivity/sshpass/sshpass_1.05.bb new file mode 100644 index 0000000..a345dbd --- /dev/null +++ b/device-software/meta-edison-middleware/recipes-connectivity/sshpass/sshpass_1.05.bb @@ -0,0 +1,8 @@ +PR = "${INC_PR}.0" + +require sshpass.inc + +LIC_FILES_CHKSUM = "file://COPYING;md5=94d55d512a9ba36caa9b7df079bae19f" + +SRC_URI[md5sum] = "c52d65fdee0712af6f77eb2b60974ac7" +SRC_URI[sha256sum] = "c3f78752a68a0c3f62efb3332cceea0c8a1f04f7cf6b46e00ec0c3000bc8483e" diff --git a/device-software/meta-edison-middleware/recipes-connectivity/zeromq/cppzmq_git.bb b/device-software/meta-edison-middleware/recipes-connectivity/zeromq/cppzmq_git.bb new file mode 100644 index 0000000..26e89b1 --- /dev/null +++ b/device-software/meta-edison-middleware/recipes-connectivity/zeromq/cppzmq_git.bb @@ -0,0 +1,19 @@ +DESCRIPTION = "C++ bindings for ZeroMQ" +HOMEPAGE = "http://www.zeromq.org" +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://LICENSE;md5=db174eaf7b55a34a7c89551197f66e94" +DEPENDS = "zeromq" +SRCREV = "ee47ae4cddc304741526c9bb2035f98c3274e0ec" + +SRC_URI = "git://github.com/zeromq/cppzmq.git" + +S = "${WORKDIR}/git" + +do_install () { + install -d ${D}/usr/include + install -m 0755 ${S}/zmq.hpp ${D}/usr/include/ +} + +PACKAGES = "${PN}-dev" + +RDEPENDS_${PN}-dev = "zeromq-dev" diff --git a/device-software/meta-edison-middleware/recipes-connectivity/zeromq/zeromq_4.0.4.bb b/device-software/meta-edison-middleware/recipes-connectivity/zeromq/zeromq_4.0.4.bb index 64235d4..2b6b225 100644 --- a/device-software/meta-edison-middleware/recipes-connectivity/zeromq/zeromq_4.0.4.bb +++ b/device-software/meta-edison-middleware/recipes-connectivity/zeromq/zeromq_4.0.4.bb @@ -2,7 +2,7 @@ DESCRIPTION = "Zeromq - The Intelligent Transport Layer" HOMEPAGE = "http://www.zeromq.org" LICENSE = "LGPLv3+" -PR = "r1" +PR = "r2" LIC_FILES_CHKSUM = " \ file://COPYING.LESSER;md5=d5311495d952062e0e4fbba39cbf3de1 \ @@ -13,7 +13,3 @@ SRC_URI[md5sum] = "f3c3defbb5ef6cc000ca65e529fdab3b" SRC_URI[sha256sum] = "1ef71d46e94f33e27dd5a1661ed626cd39be4d2d6967792a275040e34457d399" inherit autotools gettext - -do_configure_prepend() { - ./autogen.sh -} diff --git a/device-software/meta-edison-middleware/recipes-core/images/edison-image.bbappend b/device-software/meta-edison-middleware/recipes-core/images/edison-image.bbappend index 976bdf3..c43c465 100644 --- a/device-software/meta-edison-middleware/recipes-core/images/edison-image.bbappend +++ b/device-software/meta-edison-middleware/recipes-core/images/edison-image.bbappend @@ -1,19 +1,28 @@ IMAGE_INSTALL += "packagegroup-core-buildessential" -IMAGE_INSTALL += "zeromq zeromq-dev" -IMAGE_INSTALL += "rsmb" -IMAGE_INSTALL += "nodejs" -IMAGE_INSTALL += "paho-mqtt paho-mqtt-dev" -IMAGE_INSTALL += "mdns mdns-dev" +IMAGE_INSTALL += "iotkit-opkg" +IMAGE_INSTALL += "zeromq-dev" +IMAGE_INSTALL += "cppzmq-dev" +IMAGE_INSTALL += "paho-mqtt-dev" +IMAGE_INSTALL += "mdns-dev" IMAGE_INSTALL += "iotkit-comm-js" -IMAGE_INSTALL += "iotkit-comm-c iotkit-comm-c-dev" +IMAGE_INSTALL += "iotkit-comm-c-dev" IMAGE_INSTALL += "iotkit-agent" +IMAGE_INSTALL += "iotkit-lib-c-dev" IMAGE_INSTALL += "xdk-daemon" IMAGE_INSTALL += "oobe" +# mosquitto and dependencies +IMAGE_INSTALL += "mosquitto-dev" +IMAGE_INSTALL += "mosquitto-clients" + +# node and sub-components +IMAGE_INSTALL += "nodejs-dev" +IMAGE_INSTALL += "nodejs-npm" + # MRAA -IMAGE_INSTALL += "mraa mraa-dev" +IMAGE_INSTALL += "mraa-dev" IMAGE_INSTALL += "mraa-doc" # UPM -IMAGE_INSTALL += "upm upm-dev" +IMAGE_INSTALL += "upm-dev" diff --git a/device-software/meta-edison-middleware/recipes-devtools/iotkit-agent/iotkit-agent_0.2.0.bb b/device-software/meta-edison-middleware/recipes-devtools/iotkit-agent/iotkit-agent_0.2.0.bb new file mode 100644 index 0000000..29d7d70 --- /dev/null +++ b/device-software/meta-edison-middleware/recipes-devtools/iotkit-agent/iotkit-agent_0.2.0.bb @@ -0,0 +1,84 @@ +DESCRIPTION = "Transparently implements the necessary message formats and transport security as well as device registration" +HOMEPAGE = "http://enableiot.com" +LICENSE = "BSD-2-Clause & BSD-3-Clause & GPL-2.0 & Apache-2.0 & MIT & PD" + +LIC_FILES_CHKSUM = "file://${WORKDIR}/git/LICENSE;md5=30c8ae0368f724cf5f753d08bf033034" + +DEPENDS = "nodejs-native" + +SRC_URI = "git://github.com/intel-iot-devkit/iotkit-agent.git;protocol=https;branch=dprelease;tag=prod-v${PV}" + +S = "${WORKDIR}/git" + +do_compile () { + # changing the home directory to the working directory, the .npmrc will be created in this directory + export HOME=${WORKDIR} + + # does not build dev packages + npm config set dev false + + # access npm registry using http + npm set strict-ssl false + npm config set registry http://registry.npmjs.org/ + + # configure http proxy if neccessary + if [ -n "${http_proxy}" ]; then + npm config set proxy ${http_proxy} + fi + if [ -n "${HTTP_PROXY}" ]; then + npm config set proxy ${HTTP_PROXY} + fi + + # configure cache to be in working directory + npm set cache ${WORKDIR}/npm_cache + + # clear local cache prior to each compile + npm cache clear + + # compile and install node modules in source directory + npm --arch=${TARGET_ARCH} --production --verbose install +} + +do_install () { + install -d ${D}${libdir} + install -d ${D}${libdir}/node_modules/ + install -d ${D}${libdir}/node_modules/iotkit-agent/ + install -d ${D}${sysconfdir}/iotkit-agent/ + install -d ${D}${libdir}/node_modules/iotkit-agent/config/ + install -m 0644 ${S}/package.json ${D}${libdir}/node_modules/iotkit-agent/ + install -m 0644 ${S}/config/config.json ${D}${sysconfdir}/iotkit-agent/ + install -d ${D}${datadir}/iotkit-agent/ + install -d ${D}${bindir} + + cp -r ${S}/node_modules ${D}${libdir}/node_modules/iotkit-agent/ + cp -r ${S}/admin ${D}${libdir}/node_modules/iotkit-agent/ + cp -r ${S}/api ${D}${libdir}/node_modules/iotkit-agent/ + cp -r ${S}/bin ${D}${libdir}/node_modules/iotkit-agent/ + cp -r ${S}/certs ${D}${datadir}/iotkit-agent/ + cp -r ${S}/data ${D}${datadir}/iotkit-agent/ + cp -r ${S}/lib ${D}${libdir}/node_modules/iotkit-agent/ + cp -r ${S}/listeners ${D}${libdir}/node_modules/iotkit-agent/ + install -m 0644 ${S}/config/index.js ${D}${libdir}/node_modules/iotkit-agent/config + install -m 0755 ${S}/iotkit-admin.js ${D}${libdir}/node_modules/iotkit-agent/ + install -m 0755 ${S}/iotkit-agent.js ${D}${libdir}/node_modules/iotkit-agent/ + ln -s ../lib/node_modules/iotkit-agent/iotkit-agent.js ${D}${bindir}/iotkit-agent + ln -s ../lib/node_modules/iotkit-agent/iotkit-admin.js ${D}${bindir}/iotkit-admin + + install -d ${D}${systemd_unitdir}/system/ + install -m 0644 ${S}/iotkit-agent.service ${D}${systemd_unitdir}/system/ +} + +inherit systemd + +# since the agent requires registration before running we don't want to start +# the systemd service by default +SYSTEMD_AUTO_ENABLE = "disable" +SYSTEMD_SERVICE_${PN} = "iotkit-agent.service" + +FILES_${PN} = "${libdir}/node_modules/ \ + ${bindir}/iotkit-agent \ + ${bindir}/iotkit-admin \ + ${datadir}/iotkit-agent/ \ + ${sysconfdir}/iotkit-agent/" + +PACKAGES = "${PN}" diff --git a/device-software/meta-edison-middleware/recipes-devtools/iotkit-comm-c/iotkit-comm-c_0.1.1.bb b/device-software/meta-edison-middleware/recipes-devtools/iotkit-comm-c/iotkit-comm-c_0.1.1.bb index ffcb8f3..c1c0260 100644 --- a/device-software/meta-edison-middleware/recipes-devtools/iotkit-comm-c/iotkit-comm-c_0.1.1.bb +++ b/device-software/meta-edison-middleware/recipes-devtools/iotkit-comm-c/iotkit-comm-c_0.1.1.bb @@ -1,12 +1,17 @@ DESCRIPTION = "Inter of Things communication library for device-to-device and device-to-cloud messaging" -LICENSE = "LGPLv2.1" +LICENSE = "MIT" -S = "${EDISONREPO_TOP_DIR}/mw/iecf-c" +PR = "r2" + +SRC_URI = "git://github.com/intel-iot-devkit/iotkit-comm-c.git;protocol=git" +SRCREV = "2367175b07852a8d2600677f5d893af49973f422" LIC_FILES_CHKSUM = " \ - file://LICENSE;md5=1a6d268fd218675ffea8be556788b780 \ + file://COPYING;md5=e8db6501ed294e65418a933925d12058 \ " +S = "${WORKDIR}/git" + DEPENDS = "zeromq mdns paho-mqtt" inherit pkgconfig cmake @@ -33,7 +38,9 @@ do_install() { install -d ${D}${libdir} install ${B}/lib/libiotkit-comm/libiotkit-comm.so ${D}${libdir}/ install ${B}/lib/plugins/libiotkitpubsub/libiotkit-agent-client.so ${D}${libdir}/ + install ${B}/lib/plugins/libiotkitpubsub/libiotkit-agent-service.so ${D}${libdir}/ install ${B}/lib/plugins/libmqttpubsub/libmqttpubsub-client.so ${D}${libdir}/ + install ${B}/lib/plugins/libmqttpubsub/libmqttpubsub-service.so ${D}${libdir}/ install ${B}/lib/plugins/libzmqpubsub/libzmqpubsub-client.so ${D}${libdir}/ install ${B}/lib/plugins/libzmqpubsub/libzmqpubsub-service.so ${D}${libdir}/ install ${B}/lib/plugins/libzmqreqrep/libzmqreqrep-client.so ${D}${libdir}/ @@ -97,10 +104,10 @@ do_install() { } FILES_${PN} += "${libdir}" -RDEPENDS_${PN} = "zeromq mdns paho-mqtt rsmb" +RDEPENDS_${PN} = "zeromq mdns paho-mqtt mosquitto sshpass" PACKAGES += "${PN}-tests" -RDEPENDS_${PN}-tests += "${PN} gcov" +RDEPENDS_${PN}-tests += "${PN} gcov cmake" FILES_${PN}-dev = "${includedir} ${datadir}/iotkit-comm/examples/c/" FILES_${PN}-dbg += "${datadir}/iotkit-comm/examples/c/.debug/ ${datadir}/iotkit-comm/tests/c/iotkitpubsub/.debug/ ${datadir}/iotkit-comm/tests/c/mqttpubsub/.debug/ ${datadir}/iotkit-comm/tests/c/zmqpubsub/.debug/ ${datadir}/iotkit-comm/tests/c/libiotkit-comm/.debug/ ${datadir}/iotkit-comm/tests/c/zmqreqrep/.debug/" diff --git a/device-software/meta-edison-middleware/recipes-devtools/iotkit-comm-js/iotkit-comm-js_0.1.1.bb b/device-software/meta-edison-middleware/recipes-devtools/iotkit-comm-js/iotkit-comm-js_0.1.1.bb index 864e46e..8b0d20f 100644 --- a/device-software/meta-edison-middleware/recipes-devtools/iotkit-comm-js/iotkit-comm-js_0.1.1.bb +++ b/device-software/meta-edison-middleware/recipes-devtools/iotkit-comm-js/iotkit-comm-js_0.1.1.bb @@ -1,12 +1,17 @@ DESCRIPTION = "Inter of Things communication library for device-to-device and device-to-cloud messaging" -LICENSE = "LGPLv2.1" +LICENSE = "MIT" -S = "${EDISONREPO_TOP_DIR}/mw/iecf-js" +PR = "r2" + +SRC_URI = "git://github.com/intel-iot-devkit/iotkit-comm-js.git;protocol=git" +SRCREV = "5ccea56e88755c9f6e3cd37bb5fc5d747b8496aa" LIC_FILES_CHKSUM = " \ - file://LICENSE;md5=1a6d268fd218675ffea8be556788b780 \ + file://COPYING;md5=e8db6501ed294e65418a933925d12058 \ " +S = "${WORKDIR}/git" + DEPENDS = "nodejs-native zeromq mdns paho-mqtt" do_compile () { @@ -35,14 +40,14 @@ do_compile () { npm cache clear # compile and install node modules in source directory - npm --arch=${TARGET_ARCH} --production --verbose install + npm --arch=${TARGET_ARCH} --verbose install } do_install () { install -d ${D}${libdir}/node_modules/iotkit-comm/ cp -r ${S}/node_modules ${D}${libdir}/node_modules/iotkit-comm/ install -m 644 ${S}/package.json ${D}${libdir}/node_modules/iotkit-comm/ - install -m 644 ${S}/LICENSE ${D}${libdir}/node_modules/iotkit-comm/ + install -m 644 ${S}/COPYING ${D}${libdir}/node_modules/iotkit-comm/ install -m 644 ${S}/README.md ${D}${libdir}/node_modules/iotkit-comm/ install -m 644 ${S}/jsdoc-conf.json ${D}${libdir}/node_modules/iotkit-comm/ cp -r ${S}/lib ${D}${libdir}/node_modules/iotkit-comm/ @@ -50,12 +55,46 @@ do_install () { cp -r ${S}/doc ${D}${libdir}/node_modules/iotkit-comm/ install -d ${D}${datadir}/iotkit-comm/examples/node cp -r ${S}/example/* ${D}${datadir}/iotkit-comm/examples/node + + chmod 755 ${D}${libdir}/node_modules/iotkit-comm/lib/setup.js + install -d ${D}${bindir} + ln -s ../lib/node_modules/iotkit-comm/lib/setup.js ${D}${bindir}/iotkit-comm } -INHIBIT_PACKAGE_DEBUG_SPLIT = "1" INHIBIT_PACKAGE_STRIP = "1" -FILES_${PN} = "${libdir}/node_modules/ ${datadir}/iotkit-comm/examples" -RDEPENDS_${PN} = "nodejs zeromq mdns paho-mqtt rsmb" +PACKAGES = "${PN} ${PN}-test-dependencies" + +FILES_${PN}-test-dependencies = " \ + ${libdir}/node_modules/iotkit-comm/node_modules/.bin/istanbul \ + ${libdir}/node_modules/iotkit-comm/node_modules/.bin/jsdoc \ + ${libdir}/node_modules/iotkit-comm/node_modules/.bin/mocha \ + ${libdir}/node_modules/iotkit-comm/node_modules/.bin/_mocha \ + ${libdir}/node_modules/iotkit-comm/node_modules/chai/ \ + ${libdir}/node_modules/iotkit-comm/node_modules/istanbul/ \ + ${libdir}/node_modules/iotkit-comm/node_modules/jsdoc/ \ + ${libdir}/node_modules/iotkit-comm/node_modules/mocha/ \ +" + +FILES_${PN} = " \ + ${libdir}/node_modules/iotkit-comm/doc/ \ + ${libdir}/node_modules/iotkit-comm/jsdoc-conf.json \ + ${libdir}/node_modules/iotkit-comm/COPYING \ + ${libdir}/node_modules/iotkit-comm/lib/ \ + ${libdir}/node_modules/iotkit-comm/package.json \ + ${libdir}/node_modules/iotkit-comm/README.md \ + ${libdir}/node_modules/iotkit-comm/test \ + ${libdir}/node_modules/iotkit-comm/node_modules/.bin/mqtt_pub \ + ${libdir}/node_modules/iotkit-comm/node_modules/.bin/mqtt_sub \ + ${libdir}/node_modules/iotkit-comm/node_modules/async/ \ + ${libdir}/node_modules/iotkit-comm/node_modules/commander/ \ + ${libdir}/node_modules/iotkit-comm/node_modules/mdns2/ \ + ${libdir}/node_modules/iotkit-comm/node_modules/mqtt/ \ + ${libdir}/node_modules/iotkit-comm/node_modules/read/ \ + ${libdir}/node_modules/iotkit-comm/node_modules/zmq/ \ + ${datadir}/iotkit-comm/examples/ \ + ${bindir}/iotkit-comm \ +" -PACKAGES = "${PN}" +RDEPENDS_${PN} = "nodejs zeromq mdns paho-mqtt mosquitto sshpass" +RDEPENDS_${PN}-test-dependencies = "${PN}" diff --git a/device-software/meta-edison-middleware/recipes-devtools/iotkit-lib-c/iotkit-lib-c_0.1.0.bb b/device-software/meta-edison-middleware/recipes-devtools/iotkit-lib-c/iotkit-lib-c_0.1.0.bb new file mode 100644 index 0000000..629cee7 --- /dev/null +++ b/device-software/meta-edison-middleware/recipes-devtools/iotkit-lib-c/iotkit-lib-c_0.1.0.bb @@ -0,0 +1,49 @@ +SUMMARY = "Transparently implements the necessary message formats and transport security as well as device registration" +SECTION = "libs" +HOMEPAGE = "http://enableiot.com" +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://COPYING;md5=e8db6501ed294e65418a933925d12058" + +DEPENDS = "nodejs-native swig-native curl" + +PR = "r1" + +SRC_URI = "git://github.com/enableiot/iotkit-lib-c.git;protocol=https;tag=v1.4.0" + +S = "${WORKDIR}/git" + +EXTRA_OECMAKE += " -DSTAGING_DIR_TARGET=${STAGING_DIR_TARGET}" + +inherit distutils-base pkgconfig python-dir cmake + +do_compile_prepend () { + # when yocto builds in ${D} it does not have access to ../git/.git so git + # describe --tags fails. In order not to tag our version as dirty we use this + # trick + sed -i 's/-dirty//' src/version.c +} + +do_install_prepend () { + # Copy config file + install -d ${D}${sysconfdir}/iotkit-lib + install -d ${D}${datadir}/iotkit-lib + install -m 644 ${S}/config/config.json ${D}${sysconfdir}/iotkit-lib/ + + # Copy test programs + cp -r ${B}/tests/ ${D}${datadir}/iotkit-lib/ + rm -rf ${D}${datadir}/iotkit-lib/tests/CMakeFiles/ + touch ${D}${datadir}/iotkit-lib/.setup.done +} + +PACKAGES =+ "${PN}-tests" + +FILES_${PN}-dbg += "${libdir}/node_modules/iotkitjs/.debug/ \ + ${PYTHON_SITEPACKAGES_DIR}/.debug/ \ + ${datadir}/iotkit-lib/tests/.debug/" + +FILES_${PN}-tests = "${datadir}/iotkit-lib/tests/*" + +FILES_${PN} += "${sysconfdir} \ + ${datadir}/iotkit-lib/.setup.done" + +RDEPENDS_${PN}-tests += "${PN} gcov cmake" diff --git a/device-software/meta-edison-middleware/recipes-devtools/iotkit-opkg/files/iotkit.conf b/device-software/meta-edison-middleware/recipes-devtools/iotkit-opkg/files/iotkit.conf new file mode 100644 index 0000000..a44c5c0 --- /dev/null +++ b/device-software/meta-edison-middleware/recipes-devtools/iotkit-opkg/files/iotkit.conf @@ -0,0 +1 @@ +src iotkit http://iotdk.intel.com/repos/1.1/intelgalactic diff --git a/device-software/meta-edison-middleware/recipes-devtools/iotkit-opkg/iotkit-opkg_0.0.1.bb b/device-software/meta-edison-middleware/recipes-devtools/iotkit-opkg/iotkit-opkg_0.0.1.bb new file mode 100644 index 0000000..019c9ba --- /dev/null +++ b/device-software/meta-edison-middleware/recipes-devtools/iotkit-opkg/iotkit-opkg_0.0.1.bb @@ -0,0 +1,22 @@ +DESCRIPTION="This configures the opkg sources for the iotkit packages" +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" + +SRC_URI = " \ + file://iotkit.conf \ +" + +PR = "r0" + +do_install() { + install -d ${D}${sysconfdir}/opkg + install -m 0644 ${WORKDIR}/iotkit.conf ${D}${sysconfdir}/opkg +} + + +RDEPENDS_${PN} = "opkg" + +FILES_${PN} = "${sysconfdir}/opkg" + +PACKAGES = "${PN}" + diff --git a/device-software/meta-edison-middleware/recipes-devtools/mraa/mraa_0.5.2.bb b/device-software/meta-edison-middleware/recipes-devtools/mraa/mraa_0.5.2.bb new file mode 100644 index 0000000..787c36b --- /dev/null +++ b/device-software/meta-edison-middleware/recipes-devtools/mraa/mraa_0.5.2.bb @@ -0,0 +1,27 @@ +SUMMARY = "Low Level Skeleton Library for Communication on Intel platforms" +SECTION = "libs" +AUTHOR = "Brendan Le Foll, Tom Ingleby" + +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://COPYING;md5=e8db6501ed294e65418a933925d12058" + +DEPENDS = "nodejs swig-native" + +SRC_URI = "git://github.com/intel-iot-devkit/mraa.git;protocol=git" +SRCREV = "bb3228ad5854b5f289c32737a6a106b139e24a05" + +S = "${WORKDIR}/git" + +inherit distutils-base pkgconfig python-dir cmake + +FILES_${PN}-doc += "${datadir}/mraa/examples/" + +FILES_${PN}-dbg += "${libdir}/node_modules/mraajs/.debug/ \ + ${PYTHON_SITEPACKAGES_DIR}/.debug/" + +do_compile_prepend () { + # when yocto builds in ${D} it does not have access to ../git/.git so git + # describe --tags fails. In order not to tag our version as dirty we use this + # trick + sed -i 's/-dirty//' src/version.c +} \ No newline at end of file diff --git a/device-software/meta-edison-middleware/recipes-devtools/nodejs/nodejs_0.10.28.bb b/device-software/meta-edison-middleware/recipes-devtools/nodejs/nodejs_0.10.28.bb index c2eb76f..a463bc8 100644 --- a/device-software/meta-edison-middleware/recipes-devtools/nodejs/nodejs_0.10.28.bb +++ b/device-software/meta-edison-middleware/recipes-devtools/nodejs/nodejs_0.10.28.bb @@ -7,6 +7,8 @@ LIC_FILES_CHKSUM = "file://LICENSE;md5=4a31e6c424761191227143b86f58a1ef" DEPENDS = "openssl" DEPENDS_class-target = "nodejs-native" +PR = "r1" + SRC_URI = "git://github.com/joyent/node.git;protocol=https;branch=v0.10;tag=v0.10.28" S = "${WORKDIR}/git" @@ -20,8 +22,7 @@ ARCHFLAGS ?= "" # Node is way too cool to use proper autotools, so we install two wrappers to forcefully inject proper arch cflags to workaround gypi do_configure () { export LD="${CXX}" - - ./configure --prefix=${prefix} --without-snapshot ${ARCHFLAGS} + ./configure --prefix=${prefix} --without-snapshot --shared-openssl ${ARCHFLAGS} } do_compile () { @@ -50,6 +51,17 @@ do_install_append_class-target () { fi } +do_install_append_class-native() { + # /usr/bin/npm is symlink to /usr/lib/node_modules/npm/bin/npm-cli.js + # use sed on npm-cli.js because otherwise symlink is replaced with normal file and + # npm-cli.js continues to use old shebang + sed "1s^.*^#\!/usr/bin/env node^g" -i ${D}${libdir}/node_modules/npm/bin/npm-cli.js +} + +do_install_append_class-target() { + sed "1s^.*^#\!${bindir}/env node^g" -i ${D}${libdir}/node_modules/npm/bin/npm-cli.js +} + pkg_postinst_${PN} () { sed -e '/^PATH=/aNODE_PATH=\/usr\/lib\/node_modules\/' \ -e 's/\(^export\)\(.*\)/\1 NODE_PATH\2/' \ @@ -62,10 +74,12 @@ pkg_prerm_${PN} () { -i $D/etc/profile } -RDEPENDS_${PN} = "curl python-shell python-datetime python-subprocess python-crypt python-textutils \ - python-netclient python-ctypes python-misc python-compiler python-multiprocessing" - +RDEPENDS_${PN} = "curl" RDEPENDS_${PN}_class-native = "" -FILES_${PN} += "${libdir}/node/wafadmin ${libdir}/node_modules ${libdir}/dtrace" +PACKAGES += "${PN}-npm" +FILES_${PN}-npm = "${libdir}/node_modules ${bindir}/npm" +RDEPENDS_${PN}-npm = "python-shell python-datetime python-subprocess python-crypt python-textutils \ + python-netclient python-ctypes python-misc python-compiler python-multiprocessing" + BBCLASSEXTEND = "native" diff --git a/device-software/meta-edison-middleware/recipes-devtools/oobe/oobe_0.0.1.bb b/device-software/meta-edison-middleware/recipes-devtools/oobe/oobe_0.0.1.bb index 92b4ec2..ebf73d0 100644 --- a/device-software/meta-edison-middleware/recipes-devtools/oobe/oobe_0.0.1.bb +++ b/device-software/meta-edison-middleware/recipes-devtools/oobe/oobe_0.0.1.bb @@ -1,21 +1,48 @@ DESCRIPTION="The out-of-box configuration service" -LICENSE = "LGPLv2.1" +LICENSE = "MIT" S = "${EDISONREPO_TOP_DIR}/mw/oobe" LIC_FILES_CHKSUM = " \ - file://LICENSE;md5=1a6d268fd218675ffea8be556788b780 \ + file://LICENSE;md5=ea398a763463b76b18da15f013c0c531 \ " DEPENDS = "nodejs-native" do_compile() { + # changing the home directory to the working directory, the .npmrc will be created in this directory + export HOME=${WORKDIR} + + # does not build dev packages + npm config set dev false + + # access npm registry using http + npm set strict-ssl false + npm config set registry http://registry.npmjs.org/ + + # configure http proxy if neccessary + if [ -n "${http_proxy}" ]; then + npm config set proxy ${http_proxy} + fi + if [ -n "${HTTP_PROXY}" ]; then + npm config set proxy ${HTTP_PROXY} + fi + + # configure cache to be in working directory + npm set cache ${WORKDIR}/npm_cache + + # clear local cache prior to each compile + npm cache clear + + # compile and install node modules in source directory + npm --arch=${TARGET_ARCH} --verbose install } do_install() { install -d ${D}${libdir}/edison_config_tools install -d ${D}/var/lib/edison_config_tools cp -r ${S}/src/public ${D}${libdir}/edison_config_tools + cp -r ${S}/node_modules ${D}${libdir}/edison_config_tools install -m 0644 ${S}/src/server.js ${D}${libdir}/edison_config_tools/edison-config-server.js install -d ${D}${systemd_unitdir}/system/ install -m 0644 ${S}/src/edison_config.service ${D}${systemd_unitdir}/system/ diff --git a/device-software/meta-edison-middleware/recipes-devtools/upm/upm_0.1.8.bb b/device-software/meta-edison-middleware/recipes-devtools/upm/upm_0.1.8.bb new file mode 100644 index 0000000..b259d42 --- /dev/null +++ b/device-software/meta-edison-middleware/recipes-devtools/upm/upm_0.1.8.bb @@ -0,0 +1,17 @@ +SUMMARY = "Sensor/Actuator repository for Mraa" +SECTION = "libs" +AUTHOR = "Brendan Le Foll, Tom Ingleby, Yevgeniy Kiveisha" + +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://COPYING;md5=e8db6501ed294e65418a933925d12058" + +DEPENDS = "nodejs swig-native mraa" + +SRC_URI = "git://github.com/intel-iot-devkit/upm.git;protocol=git" +SRCREV = "88eaced5a23c23d1cfe3badfe8deeedda582ae50" + +S = "${WORKDIR}/git" + +inherit distutils-base pkgconfig python-dir cmake + +FILES_${PN}-doc += "${datadir}/upm/examples/" \ No newline at end of file diff --git a/device-software/meta-edison-middleware/recipes-devtools/xdk-daemon/xdk-daemon_0.0.27.bb b/device-software/meta-edison-middleware/recipes-devtools/xdk-daemon/xdk-daemon_0.0.27.bb new file mode 100644 index 0000000..90eb588 --- /dev/null +++ b/device-software/meta-edison-middleware/recipes-devtools/xdk-daemon/xdk-daemon_0.0.27.bb @@ -0,0 +1,68 @@ +DESCRIPTION = "Provides communication to the Intel XDK" +LICENSE = "Proprietary" + +LIC_FILES_CHKSUM = "file://LICENSE;md5=8a05f85865f8c4b9ba29798e539f93b7" + +DEPENDS = "nodejs-native mdns" +RDEPENDS_${PN} = "libarchive-bin" + +PR = "r0" + +# URI should point to some external http:// server +SRC_URI = "file://xdk-daemon-${PV}.tar.bz2" + +# we don't care about debug for the few binary node modules +INHIBIT_PACKAGE_DEBUG_SPLIT = "1" + +do_compile () { + # changing the home directory to the working directory, the .npmrc will be created in this directory + export HOME=${WORKDIR} + + # does not build dev packages + npm config set dev false + + # access npm registry using http + npm set strict-ssl false + npm config set registry http://registry.npmjs.org/ + + # configure http proxy if neccessary + if [ -n "${http_proxy}" ]; then + npm config set proxy ${http_proxy} + fi + if [ -n "${HTTP_PROXY}" ]; then + npm config set proxy ${HTTP_PROXY} + fi + + # configure cache to be in working directory + npm set cache ${WORKDIR}/npm_cache + + # clear local cache prior to each compile + npm cache clear + + npm install --arch=${TARGET_ARCH} + cd current/ && npm install --arch=${TARGET_ARCH} + cd node-inspector-server && npm install --arch=${TARGET_ARCH} + + sed -i '/TM/d' ${S}/xdk-daemon +} + +do_install () { + install -d ${D}/opt/xdk-daemon/ + cp -a ${S}/* ${D}/opt/xdk-daemon/ + + install -d ${D}${systemd_unitdir}/system/ + install -m 0644 ${S}/xdk-daemon.service ${D}${systemd_unitdir}/system/ + + install -d ${D}${bindir} + ln -s /opt/xdk-daemon/current/xdk-whitelist ${D}${bindir}/xdk-whitelist +} + +inherit systemd + +SYSTEMD_SERVICE_${PN} = "xdk-daemon.service" + +FILES_${PN} = "/opt/xdk-daemon/ \ + ${systemd_unitdir}/system/xdk-daemon.service \ + ${bindir}/" + +PACKAGES = "${PN}" diff --git a/device-software/meta-edison/conf/machine/edison.conf b/device-software/meta-edison/conf/machine/edison.conf index 0dcd102..6bc5606 100644 --- a/device-software/meta-edison/conf/machine/edison.conf +++ b/device-software/meta-edison/conf/machine/edison.conf @@ -14,7 +14,7 @@ UBOOT_MACHINE = "edison_config" module_autoload_bcm_bt_lpm = "bcm_bt_lpm" module_autoload_bcm4334x = "bcm4334x" -module_conf_bcm4334x = "options bcm4334x firmware_path=/etc/firmware/fw_bcmdhd.bin nvram_path=/etc/firmware/bcmdhd.cal" +module_conf_bcm4334x = "options bcm4334x firmware_path=/etc/firmware/fw_bcmdhd.bin nvram_path=/etc/firmware/bcmdhd.cal op_mode=4" module_autoload_g_multi = "g_multi" # FIXME: file parameter should be based on partition UUID (from U-Boot) or fixed # with label (label seems to work, but driver is probed too early) diff --git a/device-software/meta-edison/recipes-bsp/u-boot/files/edison.env b/device-software/meta-edison/recipes-bsp/u-boot/files/edison.env new file mode 100644 index 0000000..bd5a551 --- /dev/null +++ b/device-software/meta-edison/recipes-bsp/u-boot/files/edison.env @@ -0,0 +1,56 @@ +# Edison Environment File +# Main part + +# Partition definition +partitions=uuid_disk=${uuid_disk};name=u-boot0,start=1MiB,size=2MiB,uuid=${uuid_uboot0};name=u-boot-env0,size=1MiB,uuid=${uuid_uboot_env0};name=u-boot1,size=2MiB,uuid=${uuid_uboot1};name=u-boot-env1,size=1MiB,uuid=${uuid_uboot_env1};name=factory,size=1MiB,uuid=${uuid_factory};name=panic,size=24MiB,uuid=${uuid_panic};name=boot,size=32MiB,uuid=${uuid_boot};name=rootfs,size=1536MiB,uuid=${uuid_rootfs};name=update,size=768MiB,uuid=${uuid_update};name=home,size=-,uuid=${uuid_home}; + +# Dfu Alternate setting definition +do_dfu_alt_info_mmc=setenv dfu_alt_info "ifwi${hardware_id} mmc 0 8192 mmcpart 1;ifwib${hardware_id} mmc 0 8192 mmcpart 2;u-boot0 part 0 1;u-boot-env0 part 0 2;u-boot1 part 0 3;u-boot-env1 part 0 4;boot part 0 7;rootfs part 0 8;update part 0 9;home part 0 10;vmlinuz fat 0 7;initrd fat 0 7" +dfu_alt_info_ram=kernel ram ${loadaddr} 0x800000 +do_dfu_alt_info_ifwi=setenv dfu_alt_info "ifwi${hardware_id} mmc 0 8192 mmcpart 1;ifwib${hardware_id} mmc 0 8192 mmcpart 2" +dfu_alt_info_reset=reset ram 0x0 0x0 + +# Kernel load configuration +bootargs_console=console=ttyMFD2 earlyprintk=ttyMFD2,keep +bootargs_debug=loglevel=4 +do_bootargs_rootfs=setenv bootargs_rootfs rootwait root=PARTUUID=${uuid_rootfs} rootfstype=ext4 +first_install_retry=0 +first_install_max_retries=3 +ota_update_retry=0 +ota_update_max_retries=3 +do_compute_target=if itest.b ${first_install_retry} -gt ${first_install_max_retries} || itest.b ${ota_update_retry} -gt ${ota_update_max_retries}; then echo "Switch to Rescue target"; setenv bootargs_target rescue; saveenv; fi +mmc-bootargs=run do_bootargs_rootfs; setenv bootargs ${bootargs_rootfs} ${bootargs_console} ${bootargs_debug} g_multi.ethernet_config=${bootargs_ethconfig} systemd.unit=${bootargs_target}.target hardware_id=${hardware_id} g_multi.iSerialNumber=${serial#} g_multi.dev_addr=${usb0addr} +loadaddr=0x100000 +load_kernel=fatload mmc 0:7 ${loadaddr} vmlinuz + +# Main functions +do_partition_done=0 +do_partition=if itest.b ${do_partition_done} -eq 1; then echo "Partitioning already done..."; else run do_force_partition ; fi +do_force_partition=echo "Partitioning using GPT"; gpt write mmc 0 ${partitions} ; mmc rescan; setenv do_partition_done 1 ; saveenv +do_flash_ifwi=run do_dfu_alt_info_ifwi ; dfu 0 mmc 0 +do_flash_os=if itest.b ${do_flash_os_done} -eq 1 ; then echo "Flashing already done..." ; else run do_force_flash_os; fi +do_force_flash_os=run do_dfu_alt_info_mmc ; sleep 1 ; setenv do_flash_os_done 1 ; saveenv ; dfu 0 mmc 0 +do_flashall=run do_partition;run do_flash_ifwi;run do_flash_os +do_dnx=setenv dfu_alt_info ${dfu_alt_info_ram};dfu 0 ram 0 ram;run bootcmd +init_dfu=run do_dfu_alt_info_mmc ; saveenv + +# Handle different boot mode +bootcmd=echo "Target:${target_name}"; run do_partition; run do_handle_bootargs_mode; + +do_handle_bootargs_mode=run do_preprocess_bootargs_mode; if itest.s $bootargs_mode == "ota" ; then run do_ota; fi; if itest.s $bootargs_mode == "boot" ; then run do_boot; fi; if itest.s $bootargs_mode == "flash"; then run do_flash; fi; run do_fallback; exit; +do_preprocess_bootargs_mode=if env exists bootargs_mode ; then ; else setenv bootargs_mode "boot" ;fi; + +do_fallback=echo "Unknown boot mode: $bootargs_mode"; env delete -f bootargs_mode; saveenv; echo "Resetting to default boot mode and reboot..."; reset; +do_boot=run boot_target_cmd; +do_flash=run do_force_flash_os; + +# OTA settings +ota_script_addr=0x100000 +do_ota_init=setenv ota_status 1 ; env delete -f bootargs_mode +do_load_ota_scr=if fatload mmc 0:9 $ota_script_addr ota_update.scr ; then setenv ota_status 0 ; else setenv ota_status 1 ; fi +do_source_ota_scr=if test $ota_status -eq 0 ; then if source $ota_script_addr ; then setenv ota_status 0 ; else setenv ota_status 2 ; fi ; fi + +# do_ota_clean can be overriden by ota script +do_ota_clean=saveenv ; reset + +do_ota=run do_ota_init ; run do_load_ota_scr ; run do_source_ota_scr ; run do_ota_clean diff --git a/device-software/meta-edison/recipes-bsp/u-boot/files/fw_env.config b/device-software/meta-edison/recipes-bsp/u-boot/files/fw_env.config new file mode 100644 index 0000000..392bb8e --- /dev/null +++ b/device-software/meta-edison/recipes-bsp/u-boot/files/fw_env.config @@ -0,0 +1,11 @@ +# Configuration file for fw_(printenv/setenv) utility. +# Up to two entries are valid, in this case the redundant +# environment sector is assumed present. +# Notice, that the "Number of sectors" is not required on NOR and SPI-dataflash. +# Futhermore, if the Flash sector size is ommitted, this value is assumed to +# be the same as the Environment size, which is valid for NOR and SPI-dataflash + +# MTD device name Device offset Env. size Flash sector size Number of sectors +# On Edison, the u-boot environments are located on partitions 2 and 4 and both have a size of 64kB +/dev/mmcblk0p2 0x0000 0x10000 +/dev/mmcblk0p4 0x0000 0x10000 diff --git a/device-software/meta-edison/recipes-bsp/u-boot/files/target_env/blankcdc.env b/device-software/meta-edison/recipes-bsp/u-boot/files/target_env/blankcdc.env new file mode 100644 index 0000000..a04d786 --- /dev/null +++ b/device-software/meta-edison/recipes-bsp/u-boot/files/target_env/blankcdc.env @@ -0,0 +1,10 @@ +# U-Boot blank environment with CDC ECM ethernet config for gadget multi +# used to erase all partitions and first boot setup +target_name=blank +bootdelay=1 +do_flash_os_done=1 +bootargs_target=first-install +bootargs_ethconfig=cdc +dfu_to_sec=3 +do_probe_dfu=run do_dfu_alt_info_mmc ; dfu 0 mmc 0 $dfu_to_sec +boot_target_cmd=run do_flash_os;run do_probe_dfu;run do_compute_target;run mmc-bootargs;run load_kernel;zboot ${loadaddr} diff --git a/device-software/meta-edison/recipes-bsp/u-boot/files/target_env/blankrndis.env b/device-software/meta-edison/recipes-bsp/u-boot/files/target_env/blankrndis.env new file mode 100644 index 0000000..1fcf1ba --- /dev/null +++ b/device-software/meta-edison/recipes-bsp/u-boot/files/target_env/blankrndis.env @@ -0,0 +1,10 @@ +# U-Boot blank environment with RNDIS ethernet config for gadget multi +# used to erase all partitions and first boot setup +target_name=blank +bootdelay=1 +do_flash_os_done=1 +bootargs_target=first-install +bootargs_ethconfig=rndis +dfu_to_sec=3 +do_probe_dfu=run do_dfu_alt_info_mmc ; dfu 0 mmc 0 $dfu_to_sec +boot_target_cmd=run do_flash_os;run do_probe_dfu;run do_compute_target;run mmc-bootargs;run load_kernel;zboot ${loadaddr} diff --git a/device-software/meta-edison/recipes-bsp/u-boot/files/target_env/defaultcdc.env b/device-software/meta-edison/recipes-bsp/u-boot/files/target_env/defaultcdc.env new file mode 100644 index 0000000..6a0edda --- /dev/null +++ b/device-software/meta-edison/recipes-bsp/u-boot/files/target_env/defaultcdc.env @@ -0,0 +1,10 @@ +# U-Boot default environment +# default end-user environment +target_name=default +bootdelay=1 +do_flash_os_done=1 +bootargs_target=multi-user +bootargs_ethconfig=cdc +dfu_to_sec=3 +do_probe_dfu=run do_dfu_alt_info_mmc ; dfu 0 mmc 0 $dfu_to_sec +boot_target_cmd=run do_flash_os;run do_probe_dfu;run do_compute_target;run mmc-bootargs;run load_kernel;zboot ${loadaddr} diff --git a/device-software/meta-edison/recipes-bsp/u-boot/files/target_env/defaultrndis.env b/device-software/meta-edison/recipes-bsp/u-boot/files/target_env/defaultrndis.env new file mode 100644 index 0000000..84768c5 --- /dev/null +++ b/device-software/meta-edison/recipes-bsp/u-boot/files/target_env/defaultrndis.env @@ -0,0 +1,10 @@ +# U-Boot default environment +# default end-user environment +target_name=default +bootdelay=1 +do_flash_os_done=1 +bootargs_target=multi-user +bootargs_ethconfig=rndis +dfu_to_sec=3 +do_probe_dfu=run do_dfu_alt_info_mmc ; dfu 0 mmc 0 $dfu_to_sec +boot_target_cmd=run do_flash_os;run do_probe_dfu;run do_compute_target;run mmc-bootargs;run load_kernel;zboot ${loadaddr} diff --git a/device-software/meta-edison/recipes-bsp/u-boot/files/target_env/ifwi.env b/device-software/meta-edison/recipes-bsp/u-boot/files/target_env/ifwi.env new file mode 100644 index 0000000..e0a1286 --- /dev/null +++ b/device-software/meta-edison/recipes-bsp/u-boot/files/target_env/ifwi.env @@ -0,0 +1,7 @@ +# U-Boot IFWI environment +# IFWI testing mode +# this env is stitched with xfstk binary , do only dfu +target_name=ifwi +bootdelay=1 +do_flash_os_done=0 +boot_target_cmd=run do_partition;run do_force_flash_os diff --git a/device-software/meta-edison/recipes-bsp/u-boot/files/target_env/prod.env b/device-software/meta-edison/recipes-bsp/u-boot/files/target_env/prod.env new file mode 100644 index 0000000..7e68939 --- /dev/null +++ b/device-software/meta-edison/recipes-bsp/u-boot/files/target_env/prod.env @@ -0,0 +1,6 @@ +# U-Boot production environment +# used in factory for flashing and calibrating +target_name=prod +bootdelay=3 +do_flash_os_done=0 +boot_target_cmd=run do_flash_ifwi;run do_force_flash_os diff --git a/device-software/meta-edison/recipes-bsp/u-boot/files/upstream_to_edison.patch b/device-software/meta-edison/recipes-bsp/u-boot/files/upstream_to_edison.patch new file mode 100644 index 0000000..a44e3d1 --- /dev/null +++ b/device-software/meta-edison/recipes-bsp/u-boot/files/upstream_to_edison.patch @@ -0,0 +1,11157 @@ +From 084994e3140fb25fc9e0c96b48d03693bee7b28b Mon Sep 17 00:00:00 2001 +From: Jenkins +Date: Fri, 30 Jan 2015 14:23:54 +0100 +Subject: [PATCH] Squashed all commits from upstream to Edison + +Change-Id: I1966c64a6433cfe814361fe44e46a8313db9e653 +--- + Makefile | 1 + + arch/x86/config.mk | 4 +- + arch/x86/cpu/config.mk | 2 +- + arch/x86/cpu/start.S | 2 + + arch/x86/cpu/tangier/Makefile | 1 + + arch/x86/cpu/tangier/board_id.c | 33 + + arch/x86/cpu/tangier/car.S | 13 + + arch/x86/cpu/tangier/pci.c | 49 + + arch/x86/cpu/tangier/sdram.c | 139 ++ + arch/x86/cpu/tangier/tables.c | 26 + + arch/x86/cpu/tangier/tangier.c | 171 ++ + arch/x86/include/asm/apic.h | 91 + + arch/x86/include/asm/arch-tangier/clk.h | 3 + + arch/x86/include/asm/arch-tangier/intel-mid.h | 29 + + arch/x86/include/asm/arch-tangier/ipchecksum.h | 37 + + arch/x86/include/asm/arch-tangier/mmc.h | 11 + + arch/x86/include/asm/arch-tangier/sysinfo.h | 62 + + arch/x86/include/asm/arch-tangier/tables.h | 294 ++++ + arch/x86/include/asm/arch-tangier/timestamp.h | 59 + + arch/x86/include/asm/delay.h | 6 + + arch/x86/include/asm/io.h | 48 + + arch/x86/include/asm/mpspec.h | 74 + + arch/x86/include/asm/msr-index.h | 46 +- + arch/x86/include/asm/sfi.h | 119 ++ + arch/x86/lib/Makefile | 1 + + arch/x86/lib/sfi.c | 162 ++ + arch/x86/lib/tsc_timer.c | 6 +- + arch/x86/lib/zimage.c | 4 + + board/intel/edison/Makefile | 1 + + board/intel/edison/config.mk | 7 + + board/intel/edison/edison.c | 54 + + board/intel/edison/edison_start.S | 13 + + boards.cfg | 1 + + common/Makefile | 2 + + common/board_f.c | 20 +- + common/cmd_dfu.c | 46 +- + common/cmd_fastboot.c | 28 + + common/cmd_gpt.c | 2 +- + common/cmd_itest.c | 3 +- + common/cmd_part.c | 135 +- + disk/part_efi.c | 8 +- + drivers/dfu/dfu.c | 12 + + drivers/dfu/dfu_mmc.c | 48 +- + drivers/misc/Makefile | 1 + + drivers/misc/intel_scu_ipc.c | 150 ++ + drivers/mmc/Makefile | 1 + + drivers/mmc/tangier_sdhci.c | 38 + + drivers/serial/Makefile | 1 + + drivers/serial/serial.c | 1 + + drivers/serial/serial_tng.c | 252 +++ + drivers/usb/dwc3/Kconfig | 26 + + drivers/usb/dwc3/Makefile | 8 + + drivers/usb/dwc3/core.h | 814 +++++++++ + drivers/usb/dwc3/debug.h | 49 + + drivers/usb/dwc3/debugfs.c | 521 ++++++ + drivers/usb/dwc3/dwc3-omap.c | 418 +++++ + drivers/usb/dwc3/dwc3-pci.c | 187 ++ + drivers/usb/dwc3/dwc3_core.c | 513 ++++++ + drivers/usb/dwc3/dwc3_ep0.c | 851 +++++++++ + drivers/usb/dwc3/dwc3_gadget.c | 2184 ++++++++++++++++++++++++ + drivers/usb/dwc3/dwc3_host.c | 102 ++ + drivers/usb/dwc3/dwc3_misc.c | 20 + + drivers/usb/dwc3/gadget.h | 186 ++ + drivers/usb/dwc3/io.h | 54 + + drivers/usb/dwc3/misc.h | 269 +++ + drivers/usb/gadget/Makefile | 1 + + drivers/usb/gadget/f_dfu.c | 1 + + drivers/usb/gadget/f_fastboot.c | 559 ++++++ + drivers/usb/gadget/g_fastboot.h | 20 + + drivers/usb/gadget/u_fastboot.c | 308 ++++ + examples/standalone/Makefile | 4 + + fs/fat/fat.c | 37 +- + include/android_image.h | 102 ++ + include/configs/coreboot.h | 1 + + include/configs/edison.h | 268 +++ + include/dfu.h | 7 + + include/intel_scu_ipc.h | 69 + + include/linux/usb/gadget.h | 15 +- + include/usb/fastboot.h | 100 ++ + pft-config.xml | 28 + + 80 files changed, 9997 insertions(+), 42 deletions(-) + create mode 100644 arch/x86/cpu/tangier/Makefile + create mode 100644 arch/x86/cpu/tangier/board_id.c + create mode 100644 arch/x86/cpu/tangier/car.S + create mode 100644 arch/x86/cpu/tangier/pci.c + create mode 100644 arch/x86/cpu/tangier/sdram.c + create mode 100644 arch/x86/cpu/tangier/tables.c + create mode 100644 arch/x86/cpu/tangier/tangier.c + create mode 100644 arch/x86/include/asm/apic.h + create mode 100644 arch/x86/include/asm/arch-tangier/clk.h + create mode 100644 arch/x86/include/asm/arch-tangier/intel-mid.h + create mode 100644 arch/x86/include/asm/arch-tangier/ipchecksum.h + create mode 100644 arch/x86/include/asm/arch-tangier/mmc.h + create mode 100644 arch/x86/include/asm/arch-tangier/sysinfo.h + create mode 100644 arch/x86/include/asm/arch-tangier/tables.h + create mode 100644 arch/x86/include/asm/arch-tangier/timestamp.h + create mode 100644 arch/x86/include/asm/delay.h + create mode 100644 arch/x86/include/asm/mpspec.h + create mode 100644 arch/x86/include/asm/sfi.h + create mode 100644 arch/x86/lib/sfi.c + create mode 100644 board/intel/edison/Makefile + create mode 100644 board/intel/edison/config.mk + create mode 100644 board/intel/edison/edison.c + create mode 100644 board/intel/edison/edison.h + create mode 100644 board/intel/edison/edison_start.S + create mode 100644 common/cmd_fastboot.c + create mode 100644 drivers/misc/intel_scu_ipc.c + create mode 100644 drivers/mmc/tangier_sdhci.c + create mode 100644 drivers/serial/serial_tng.c + create mode 100644 drivers/usb/dwc3/Kconfig + create mode 100644 drivers/usb/dwc3/Makefile + create mode 100644 drivers/usb/dwc3/core.h + create mode 100644 drivers/usb/dwc3/debug.h + create mode 100644 drivers/usb/dwc3/debugfs.c + create mode 100644 drivers/usb/dwc3/dwc3-omap.c + create mode 100644 drivers/usb/dwc3/dwc3-pci.c + create mode 100644 drivers/usb/dwc3/dwc3_core.c + create mode 100644 drivers/usb/dwc3/dwc3_ep0.c + create mode 100644 drivers/usb/dwc3/dwc3_gadget.c + create mode 100644 drivers/usb/dwc3/dwc3_host.c + create mode 100644 drivers/usb/dwc3/dwc3_misc.c + create mode 100644 drivers/usb/dwc3/gadget.h + create mode 100644 drivers/usb/dwc3/io.h + create mode 100644 drivers/usb/dwc3/misc.h + create mode 100644 drivers/usb/gadget/f_fastboot.c + create mode 100644 drivers/usb/gadget/g_fastboot.h + create mode 100644 drivers/usb/gadget/u_fastboot.c + create mode 100644 include/android_image.h + create mode 100644 include/configs/edison.h + create mode 100644 include/intel_scu_ipc.h + create mode 100644 include/usb/fastboot.h + create mode 100644 pft-config.xml + +diff --git a/Makefile b/Makefile +index c91c10e..3846d2c 100644 +--- a/Makefile ++++ b/Makefile +@@ -612,6 +612,7 @@ libs-y += drivers/usb/gadget/ + libs-y += drivers/usb/host/ + libs-y += drivers/usb/musb/ + libs-y += drivers/usb/musb-new/ ++libs-y += drivers/usb/dwc3/ + libs-y += drivers/usb/phy/ + libs-y += drivers/usb/ulpi/ + libs-y += common/ +diff --git a/arch/x86/config.mk b/arch/x86/config.mk +index 38cb7c9..9048a50 100644 +--- a/arch/x86/config.mk ++++ b/arch/x86/config.mk +@@ -15,7 +15,8 @@ PF_CPPFLAGS_X86 := $(call cc-option, -fno-toplevel-reorder, \ + $(call cc-option, -mpreferred-stack-boundary=2) + PLATFORM_CPPFLAGS += $(PF_CPPFLAGS_X86) + PLATFORM_CPPFLAGS += -fno-dwarf2-cfi-asm +-PLATFORM_CPPFLAGS += -DREALMODE_BASE=0x7c0 ++#PLATFORM_CPPFLAGS += -DREALMODE_BASE=0x7c0 ++PLATFORM_CPPFLAGS += -m32 + + # Support generic board on x86 + __HAVE_ARCH_GENERIC_BOARD := y +@@ -23,6 +24,7 @@ __HAVE_ARCH_GENERIC_BOARD := y + PLATFORM_RELFLAGS += -ffunction-sections -fvisibility=hidden + + PLATFORM_LDFLAGS += --emit-relocs -Bsymbolic -Bsymbolic-functions ++PLATFORM_LDFLAGS += -m elf_i386 + + LDFLAGS_FINAL += --gc-sections -pie + LDFLAGS_FINAL += --wrap=__divdi3 --wrap=__udivdi3 +diff --git a/arch/x86/cpu/config.mk b/arch/x86/cpu/config.mk +index c1568cac..42a4425 100644 +--- a/arch/x86/cpu/config.mk ++++ b/arch/x86/cpu/config.mk +@@ -5,7 +5,7 @@ + # SPDX-License-Identifier: GPL-2.0+ + # + +-CROSS_COMPILE ?= i386-linux- ++#CROSS_COMPILE ?= i386-linux- + + PLATFORM_CPPFLAGS += -DCONFIG_X86 -D__I386__ -march=i386 -Werror + +diff --git a/arch/x86/cpu/start.S b/arch/x86/cpu/start.S +index 329bb3a..e4d1c53 100644 +--- a/arch/x86/cpu/start.S ++++ b/arch/x86/cpu/start.S +@@ -50,6 +50,7 @@ _start: + movw $GD_FLG_COLD_BOOT, %bx + 1: + ++#ifndef CONFIG_INHERIT_GDT + /* Load the segement registes to match the gdt loaded in start16.S */ + movl $(X86_GDT_ENTRY_32BIT_DS * X86_GDT_ENTRY_SIZE), %eax + movw %ax, %fs +@@ -57,6 +58,7 @@ _start: + movw %ax, %gs + movw %ax, %es + movw %ax, %ss ++#endif + + /* Clear the interrupt vectors */ + lidt blank_idt_ptr +diff --git a/arch/x86/cpu/tangier/Makefile b/arch/x86/cpu/tangier/Makefile +new file mode 100644 +index 0000000..2ef428b +--- /dev/null ++++ b/arch/x86/cpu/tangier/Makefile +@@ -0,0 +1 @@ ++obj-y += car.o tangier.o pci.o sdram.o tables.o +diff --git a/arch/x86/cpu/tangier/board_id.c b/arch/x86/cpu/tangier/board_id.c +new file mode 100644 +index 0000000..b13b051 +--- /dev/null ++++ b/arch/x86/cpu/tangier/board_id.c +@@ -0,0 +1,33 @@ ++#include ++#include ++#include ++ ++int _get_board_id(void) ++{ ++ u8 i = 0; ++ u8 data = -1; ++ int value = 0; ++ ++ for (i = 0x79; i <= 0x7c; i++) { ++ intel_scu_ipc_ioread8(i, &data); ++ value |= (data & 0x1) << (0x7c - i); ++ data = -1; ++ } ++ ++ printf("[SCU_IPC_DEBUG] board ID: %x\n", value); ++ ++ return value; ++} ++ ++int get_board_id(void) ++{ ++ static int clt_board_id = CLT_BOARD_NOT_IDENTIFIED; ++ ++ if (clt_board_id != CLT_BOARD_NOT_IDENTIFIED) ++ return clt_board_id; ++ ++ clt_board_id = _get_board_id(); ++ ++ return clt_board_id; ++} ++ +diff --git a/arch/x86/cpu/tangier/car.S b/arch/x86/cpu/tangier/car.S +new file mode 100644 +index 0000000..6982106 +--- /dev/null ++++ b/arch/x86/cpu/tangier/car.S +@@ -0,0 +1,13 @@ ++/* ++ * Copyright (c) 2011 The Chromium OS Authors. ++ * (C) Copyright 2010-2011 ++ * Graeme Russ, ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++.section .text ++ ++.globl car_init ++car_init: ++ jmp car_init_ret +diff --git a/arch/x86/cpu/tangier/pci.c b/arch/x86/cpu/tangier/pci.c +new file mode 100644 +index 0000000..af5ff2c +--- /dev/null ++++ b/arch/x86/cpu/tangier/pci.c +@@ -0,0 +1,49 @@ ++/* ++ * Copyright (c) 2011 The Chromium OS Authors. ++ * (C) Copyright 2008,2009 ++ * Graeme Russ, ++ * ++ * (C) Copyright 2002 ++ * Daniel Engström, Omicron Ceti AB, ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#include ++#include ++#include ++ ++static struct pci_controller tangier_hose; ++ ++static void config_pci_bridge(struct pci_controller *hose, pci_dev_t dev, ++ struct pci_config_table *table) ++{ ++ u8 secondary; ++ hose->read_byte(hose, dev, PCI_SECONDARY_BUS, &secondary); ++ hose->last_busno = max(hose->last_busno, secondary); ++ pci_hose_scan_bus(hose, secondary); ++} ++ ++static struct pci_config_table pci_tangier_config_table[] = { ++ /* vendor, device, class, bus, dev, func */ ++ { PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_BRIDGE_PCI, ++ PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, &config_pci_bridge }, ++ {} ++}; ++ ++void pci_init_board(void) ++{ ++ tangier_hose.config_table = pci_tangier_config_table; ++ tangier_hose.first_busno = 0; ++ tangier_hose.last_busno = 0; ++ ++ pci_set_region(tangier_hose.regions + 0, 0x0, 0x0, 0xffffffff, ++ PCI_REGION_MEM); ++ tangier_hose.region_count = 1; ++ ++ pci_setup_type1(&tangier_hose); ++ ++ pci_register_hose(&tangier_hose); ++ ++ pci_hose_scan(&tangier_hose); ++} +diff --git a/arch/x86/cpu/tangier/sdram.c b/arch/x86/cpu/tangier/sdram.c +new file mode 100644 +index 0000000..f18b45e +--- /dev/null ++++ b/arch/x86/cpu/tangier/sdram.c +@@ -0,0 +1,139 @@ ++/* ++ * Copyright (c) 2011 The Chromium OS Authors. ++ * (C) Copyright 2010,2011 ++ * Graeme Russ, ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef CONFIG_SFI ++#include ++#endif ++ ++DECLARE_GLOBAL_DATA_PTR; ++ ++unsigned install_e820_map(unsigned max_entries, struct e820entry *entries) ++{ ++#ifdef CONFIG_SFI ++ return sfi_setup_e820(max_entries, entries); ++#else ++ return 0; ++#endif ++} ++ ++/* ++ * This function looks for the highest region of memory lower than 4GB which ++ * has enough space for U-Boot where U-Boot is aligned on a page boundary. It ++ * overrides the default implementation found elsewhere which simply picks the ++ * end of ram, wherever that may be. The location of the stack, the relocation ++ * address, and how far U-Boot is moved by relocation are set in the global ++ * data structure. ++ */ ++ulong board_get_usable_ram_top(ulong total_size) ++{ ++ uintptr_t dest_addr = 0x000000003F4FFFFF; ++/* ++ * int i; ++ * ++ * for (i = 0; i < lib_sysinfo.n_memranges; i++) { ++ * struct memrange *memrange = &lib_sysinfo.memrange[i]; ++ * [> Force U-Boot to relocate to a page aligned address. <] ++ * uint64_t start = roundup(memrange->base, 1 << 12); ++ * uint64_t end = memrange->base + memrange->size; ++ * ++ * [> Ignore non-memory regions. <] ++ * if (memrange->type != CB_MEM_RAM) ++ * continue; ++ * ++ * [> Filter memory over 4GB. <] ++ * if (end > 0xffffffffULL) ++ * end = 0x100000000ULL; ++ * [> Skip this region if it's too small. <] ++ * if (end - start < total_size) ++ * continue; ++ * ++ * [> Use this address if it's the largest so far. <] ++ * if (end > dest_addr) ++ * dest_addr = end; ++ * } ++ * ++ * [> If no suitable area was found, return an error. <] ++ * if (!dest_addr) ++ * panic("No available memory found for relocation"); ++ */ ++ ++ return (ulong)dest_addr; ++} ++ ++int dram_init_f(void) ++{ ++/* ++ * int i; ++ * phys_size_t ram_size = 0; ++ * ++ * for (i = 0; i < lib_sysinfo.n_memranges; i++) { ++ * struct memrange *memrange = &lib_sysinfo.memrange[i]; ++ * unsigned long long end = memrange->base + memrange->size; ++ * ++ * if (memrange->type == CB_MEM_RAM && end > ram_size) ++ * ram_size = end; ++ * } ++ * gd->ram_size = ram_size; ++ * if (ram_size == 0) ++ * return -1; ++ */ ++#ifdef CONFIG_SFI ++ gd->ram_size = sfi_get_ram_size(); ++#endif ++ return 0; ++} ++ ++int dram_init_banksize(void) ++{ ++/* ++ * int i, j; ++ * ++ * if (CONFIG_NR_DRAM_BANKS) { ++ * for (i = 0, j = 0; i < lib_sysinfo.n_memranges; i++) { ++ * struct memrange *memrange = &lib_sysinfo.memrange[i]; ++ * ++ * if (memrange->type == CB_MEM_RAM) { ++ * gd->bd->bi_dram[j].start = memrange->base; ++ * gd->bd->bi_dram[j].size = memrange->size; ++ * j++; ++ * if (j >= CONFIG_NR_DRAM_BANKS) ++ * break; ++ * } ++ * } ++ * } ++ */ ++/* ++ *0: 0000000000000000-0000000000097FFF ( 0K - 608K) ram ++ *3: 0000000000100000-0000000003FFFFFF ( 1M - 64M) ram ++ *5: 0000000006000000-000000003F4FFFFF ( 96M - 1013M) ram ++ */ ++ gd->bd->bi_dram[0].start = 0x0; ++ gd->bd->bi_dram[0].size = 0x97FFF; ++ ++ gd->bd->bi_dram[1].start = 0x100000; ++ gd->bd->bi_dram[1].size = 0x3FFFFFF - gd->bd->bi_dram[1].start; ++ ++ gd->bd->bi_dram[2].start = 0x6000000; ++ gd->bd->bi_dram[2].size = 0x3F4FFFFF - gd->bd->bi_dram[2].start; ++ ++ return 0; ++} ++ ++int dram_init(void) ++{ ++ return dram_init_banksize(); ++} +diff --git a/arch/x86/cpu/tangier/tables.c b/arch/x86/cpu/tangier/tables.c +new file mode 100644 +index 0000000..0568877 +--- /dev/null ++++ b/arch/x86/cpu/tangier/tables.c +@@ -0,0 +1,26 @@ ++/* ++ * This file is part of the libpayload project. ++ * ++ * Copyright (C) 2008 Advanced Micro Devices, Inc. ++ * Copyright (C) 2009 coresystems GmbH ++ * ++ * SPDX-License-Identifier: BSD-3-Clause ++ */ ++ ++#include ++#include ++#include ++#include ++ ++/* ++ * This needs to be in the .data section so that it's copied over during ++ * relocation. By default it's put in the .bss section which is simply filled ++ * with zeroes when transitioning from "ROM", which is really RAM, to other ++ * RAM. ++ */ ++struct sysinfo_t lib_sysinfo __attribute__ ((section(".data"))); ++ ++int get_coreboot_info(struct sysinfo_t *info) ++{ ++ return 0; ++} +diff --git a/arch/x86/cpu/tangier/tangier.c b/arch/x86/cpu/tangier/tangier.c +new file mode 100644 +index 0000000..0fbe401 +--- /dev/null ++++ b/arch/x86/cpu/tangier/tangier.c +@@ -0,0 +1,171 @@ ++/* ++ * Copyright (c) 2011 The Chromium OS Authors. ++ * (C) Copyright 2008 ++ * Graeme Russ, graeme.russ@gmail.com. ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++DECLARE_GLOBAL_DATA_PTR; ++ ++/* ++ * Miscellaneous platform dependent initializations ++ */ ++int cpu_init_f(void) ++{ ++ timer_set_base(1); ++ return 0; ++} ++ ++int board_early_init_f(void) ++{ ++ return 0; ++} ++ ++int board_early_init_r(void) ++{ ++ return 0; ++} ++ ++int board_late_init(void) ++{ ++ if (!getenv("serial#")) { ++ ++ struct mmc *mmc = find_mmc_device(0); ++ unsigned char emmc_ssn[16]; ++ char ssn[33]; ++ char usb_gadget_addr[18]; ++ ++ if (mmc) { ++ int i; ++ ++ md5((unsigned char *)mmc->cid, sizeof(mmc->cid), emmc_ssn); ++ ++ for (i = 0; i < 16; i++) ++ snprintf(&(ssn[2*i]), 2, "%02x", emmc_ssn[i]); ++ ++ snprintf(&(usb_gadget_addr[0]), sizeof(usb_gadget_addr), ++ "02:00:86:%02x:%02x:%02x", emmc_ssn[13], emmc_ssn[14], ++ emmc_ssn[15]); ++ setenv("usb0addr", usb_gadget_addr); ++ setenv("serial#", ssn); ++ saveenv(); ++ } ++ } ++ ++ if (!getenv("hardware_id")) { ++ union ipc_ifwi_version v; ++ int ret; ++ char hardware_id[4]; ++ ++ ret = intel_scu_ipc_command(IPCMSG_GET_FW_REVISION, 1, ++ NULL, 0, (u32 *) &(v.raw[0]), 4); ++ if (ret < 0) { ++ printf("Can't retrieve hardware revision\n"); ++ } ++ ++ snprintf(hardware_id, sizeof(hardware_id), "%02X", v.fw.hardware_id); ++ setenv("hardware_id", hardware_id); ++ saveenv(); ++ } ++ ++ ++ return 0; ++} ++ ++void show_boot_progress(int val) ++{ ++ outb(val, 0x80); ++} ++ ++int last_stage_init(void) ++{ ++ /* ++ *if (gd->flags & GD_FLG_COLD_BOOT) ++ * timestamp_add_to_bootstage(); ++ */ ++ ++ return 0; ++} ++ ++int board_final_cleanup(void) ++{ ++ ++ return 0; ++} ++ ++void panic_puts(const char *str) ++{ ++} ++ ++int board_mmc_init(bd_t * bis) ++{ ++ int index = 0; ++ unsigned int base = CONFIG_SYS_EMMC_PORT_BASE + (0x40000 * index); ++ ++ return tangier_sdhci_init(base, index, 4); ++} ++ ++/* ovveride get_tbclk_mhz code see tsc_timer */ ++/* Get the speed of the TSC timer in MHz */ ++unsigned __attribute__((no_instrument_function)) long get_tbclk_mhz(void) ++{ ++ u32 ratio , bus_freq; ++ u64 platform_info = native_read_msr(MSR_PLATFORM_INFO); ++ u64 msr_fsb_freq = native_read_msr(MSR_FSB_FREQ); ++ ++ /* compute and correct ratio if necessary */ ++ ratio = ((platform_info >> 8) & 0xff); ++ if(!ratio) ++ { ++ ratio = 4; ++ debug("Read a zero ratio, force tsc ratio to 4 ...\n"); ++ } ++ ++ /* compute fsb */ ++ bus_freq = (u32) (msr_fsb_freq & 0x7); ++ /* lookup real bus freq in kHz according to its index */ ++ switch(bus_freq) ++ { ++ case 2: ++ bus_freq = FSB_FREQ_133SKU; ++ break; ++ case 3: ++ bus_freq = FSB_FREQ_167SKU; ++ break; ++ case 4: ++ bus_freq = FSB_FREQ_83SKU; ++ break; ++ case 5: ++ bus_freq = FSB_FREQ_400SKU; ++ break; ++ case 6: ++ bus_freq = FSB_FREQ_267SKU; ++ break; ++ case 7: ++ bus_freq = FSB_FREQ_333SKU; ++ break; ++ default: /* handle also 0 and 1 */ ++ bus_freq = FSB_FREQ_100SKU; ++ break; ++ } ++ // return Freq in Mhz ++ return ((bus_freq * ratio)/1000); ++} ++ ++void reset_cpu(ulong addr) ++{ ++ intel_scu_ipc_simple_command(IPCMSG_COLD_RESET, 0); ++} +diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h +new file mode 100644 +index 0000000..7b63f48 +--- /dev/null ++++ b/arch/x86/include/asm/apic.h +@@ -0,0 +1,91 @@ ++#ifndef _APIC_H_ ++#define _APIC_H_ ++ ++#include ++ ++#define IO_APIC_DEFAULT_PHYS_BASE 0xfec00000 ++#define APIC_DEFAULT_PHYS_BASE 0xfee00000 ++ ++#define ALL (0xff << 24) ++#define NONE (0) ++#define DISABLED (1 << 16) ++#define ENABLED (0 << 16) ++#define TRIGGER_EDGE (0 << 15) ++#define TRIGGER_LEVEL (1 << 15) ++#define POLARITY_HIGH (0 << 13) ++#define POLARITY_LOW (1 << 13) ++#define PHYSICAL_DEST (0 << 11) ++#define LOGICAL_DEST (1 << 11) ++#define ExtINT (7 << 8) ++#define NMI (4 << 8) ++#define SMI (2 << 8) ++#define INT (1 << 8) ++ ++#define SET_APIC_LOGICAL_ID(x) ((x) << 24) ++ ++#define APIC_LVR 0x30 ++#define APIC_INTEGRATED(x) ((x) & 0xF0u) ++#define GET_APIC_VERSION(x) ((x) & 0xFFu) ++#define GET_APIC_MAXLVT(x) (((x) >> 16) & 0xFFu) ++#define APIC_TASKPRI 0x80 ++#define APIC_EOI 0xB0 ++#define APIC_LDR 0xD0 ++#define APIC_DFR 0xE0 ++#define APIC_SPIV 0xF0 ++ ++#define APIC_TPRI_MASK 0xFFu ++#define APIC_LDR_MASK (0xFFu << 24) ++#define APIC_DFR_VALUE 0xFFFFFFFFul ++#define APIC_VECTOR_MASK 0x000FF ++#define APIC_SPIV_APIC_ENABLED (1 << 8) ++#define APIC_SPIV_FOCUS_DISABLED (1 << 9) ++ ++#define LOCAL_TIMER_VECTOR 0xef ++#define ERROR_APIC_VECTOR 0xfe ++#define SPURIOUS_APIC_VECTOR 0xff ++ ++#define APIC_ISR 0x100 ++#define APIC_ESR 0x280 ++#define APIC_LVTCMCI 0x2f0 ++#define APIC_DM_NMI 0x00400 ++#define APIC_LVTT 0x320 ++#define APIC_LVTTHMR 0x330 ++#define APIC_LVTPC 0x340 ++#define APIC_LVT0 0x350 ++#define APIC_LVT_TIMER_PERIODIC (1 << 17) ++#define SET_APIC_TIMER_BASE(x) (((x) << 18)) ++#define APIC_TIMER_BASE_DIV 0x2 ++#define APIC_LVT1 0x360 ++#define APIC_LVTERR 0x370 ++#define APIC_TMICT 0x380 ++#ifdef CONFIG_X86_MRFLD ++#define APIC_TMICT_INIT_CNT 0x144b50 ++#else ++#define APIC_TMICT_INIT_CNT 0xf3c00 ++#endif ++#define APIC_TMCCT 0x390 ++#define APIC_TDCR 0x3E0 ++#define APIC_TDR_DIV_TMBASE (1 << 2) ++#define APIC_TDR_DIV_1 0xB ++#define APIC_TDR_DIV_2 0x0 ++#define APIC_TDR_DIV_4 0x1 ++#define APIC_TDR_DIV_8 0x2 ++#define APIC_TDR_DIV_16 0x3 ++#define APIC_TDR_DIV_32 0x8 ++#define APIC_TDR_DIV_64 0x9 ++#define APIC_TDR_DIV_128 0xA ++#define APIC_LVT_MASKED (1 << 16) ++#define APIC_DM_EXTINT 0x00700 ++ ++#define LAPIC_ID 0x020 ++ ++extern unsigned int lapic_timer_frequency; ++ ++extern void apic_init(void); ++extern void apic_ack_irq(void); ++extern void apic_timer_setup(unsigned int clocks, int oneshot, int irqen); ++extern void apic_spurious_isr(void); ++extern void apic_mask_irq(void); ++extern void apic_unmask_irq(void); ++ ++#endif /* _APIC_H_ */ +diff --git a/arch/x86/include/asm/arch-tangier/clk.h b/arch/x86/include/asm/arch-tangier/clk.h +new file mode 100644 +index 0000000..ab887aa +--- /dev/null ++++ b/arch/x86/include/asm/arch-tangier/clk.h +@@ -0,0 +1,3 @@ ++#ifndef _CLK_H ++#define _CLK_H ++#endif +diff --git a/arch/x86/include/asm/arch-tangier/intel-mid.h b/arch/x86/include/asm/arch-tangier/intel-mid.h +new file mode 100644 +index 0000000..de4857c +--- /dev/null ++++ b/arch/x86/include/asm/arch-tangier/intel-mid.h +@@ -0,0 +1,29 @@ ++#ifndef _ASM_X86_INTEL_MID_H ++#define _ASM_X86_INTEL_MID_H ++ ++/* ++ * Penwell uses spread spectrum clock, so the freq number is not exactly ++ * the same as reported by MSR based on SDM. ++ * CLVP A0 has 100MHz FSB and CLVP B0 has 133MHz FSB. ++ */ ++#define FSB_FREQ_83SKU 83200 ++#define FSB_FREQ_100SKU 99840 ++#define FSB_FREQ_133SKU 133000 ++ ++#define FSB_FREQ_167SKU 167000 ++#define FSB_FREQ_200SKU 200000 ++#define FSB_FREQ_267SKU 267000 ++#define FSB_FREQ_333SKU 333000 ++#define FSB_FREQ_400SKU 400000 ++ ++/* Bus Select SoC Fuse value */ ++#define BSEL_SOC_FUSE_MASK 0x7 ++#define BSEL_SOC_FUSE_001 0x1 /* FSB 133MHz */ ++#define BSEL_SOC_FUSE_101 0x5 /* FSB 100MHz */ ++#define BSEL_SOC_FUSE_111 0x7 /* FSB 83MHz */ ++ ++extern unsigned int loops_per_jiffy; ++ ++extern unsigned long calibrate_tsc(void); ++ ++#endif /* _ASM_X86_INTEL_MID_H */ +diff --git a/arch/x86/include/asm/arch-tangier/ipchecksum.h b/arch/x86/include/asm/arch-tangier/ipchecksum.h +new file mode 100644 +index 0000000..1d73b4d +--- /dev/null ++++ b/arch/x86/include/asm/arch-tangier/ipchecksum.h +@@ -0,0 +1,37 @@ ++/* ++ * This file is part of the libpayload project. ++ * ++ * It has originally been taken from the FreeBSD project. ++ * ++ * Copyright (c) 2001 Charles Mott ++ * Copyright (c) 2008 coresystems GmbH ++ * 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. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. ++ */ ++ ++#ifndef _COREBOOT_IPCHECKSUM_H ++#define _COREBOOT_IPCHECKSUM_H ++ ++unsigned short ipchksum(const void *vptr, unsigned long nbytes); ++ ++#endif +diff --git a/arch/x86/include/asm/arch-tangier/mmc.h b/arch/x86/include/asm/arch-tangier/mmc.h +new file mode 100644 +index 0000000..2200a7f +--- /dev/null ++++ b/arch/x86/include/asm/arch-tangier/mmc.h +@@ -0,0 +1,11 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++int tangier_mmc_init(struct mmc *mmc); ++int tangier_sdhci_init(u32 regbase, int index, int bus_width); +diff --git a/arch/x86/include/asm/arch-tangier/sysinfo.h b/arch/x86/include/asm/arch-tangier/sysinfo.h +new file mode 100644 +index 0000000..8e4a61d +--- /dev/null ++++ b/arch/x86/include/asm/arch-tangier/sysinfo.h +@@ -0,0 +1,62 @@ ++/* ++ * This file is part of the libpayload project. ++ * ++ * Copyright (C) 2008 Advanced Micro Devices, Inc. ++ * ++ * SPDX-License-Identifier: BSD-3-Clause ++ */ ++ ++#ifndef _COREBOOT_SYSINFO_H ++#define _COREBOOT_SYSINFO_H ++ ++#include ++#include ++#include ++#include ++ ++/* Allow a maximum of 16 memory range definitions. */ ++#define SYSINFO_MAX_MEM_RANGES 16 ++/* Allow a maximum of 8 GPIOs */ ++#define SYSINFO_MAX_GPIOS 8 ++ ++struct sysinfo_t { ++ int n_memranges; ++ struct memrange { ++ unsigned long long base; ++ unsigned long long size; ++ unsigned int type; ++ } memrange[SYSINFO_MAX_MEM_RANGES]; ++ ++ u32 cmos_range_start; ++ u32 cmos_range_end; ++ u32 cmos_checksum_location; ++ u32 vbnv_start; ++ u32 vbnv_size; ++ ++ char *version; ++ char *extra_version; ++ char *build; ++ char *compile_time; ++ char *compile_by; ++ char *compile_host; ++ char *compile_domain; ++ char *compiler; ++ char *linker; ++ char *assembler; ++ ++ struct cb_framebuffer *framebuffer; ++ ++ int num_gpios; ++ struct cb_gpio gpios[SYSINFO_MAX_GPIOS]; ++ ++ void *vdat_addr; ++ u32 vdat_size; ++ void *tstamp_table; ++ void *cbmem_cons; ++ ++ struct cb_serial *serial; ++}; ++ ++extern struct sysinfo_t lib_sysinfo; ++ ++#endif +diff --git a/arch/x86/include/asm/arch-tangier/tables.h b/arch/x86/include/asm/arch-tangier/tables.h +new file mode 100644 +index 0000000..0d02fe0 +--- /dev/null ++++ b/arch/x86/include/asm/arch-tangier/tables.h +@@ -0,0 +1,294 @@ ++/* ++ * This file is part of the libpayload project. ++ * ++ * Copyright (C) 2008 Advanced Micro Devices, Inc. ++ * ++ * SPDX-License-Identifier: BSD-3-Clause ++ */ ++ ++#ifndef _COREBOOT_TABLES_H ++#define _COREBOOT_TABLES_H ++ ++#include ++ ++struct cbuint64 { ++ u32 lo; ++ u32 hi; ++}; ++ ++struct cb_header { ++ u8 signature[4]; ++ u32 header_bytes; ++ u32 header_checksum; ++ u32 table_bytes; ++ u32 table_checksum; ++ u32 table_entries; ++}; ++ ++struct cb_record { ++ u32 tag; ++ u32 size; ++}; ++ ++#define CB_TAG_UNUSED 0x0000 ++#define CB_TAG_MEMORY 0x0001 ++ ++struct cb_memory_range { ++ struct cbuint64 start; ++ struct cbuint64 size; ++ u32 type; ++}; ++ ++#define CB_MEM_RAM 1 ++#define CB_MEM_RESERVED 2 ++#define CB_MEM_ACPI 3 ++#define CB_MEM_NVS 4 ++#define CB_MEM_UNUSABLE 5 ++#define CB_MEM_VENDOR_RSVD 6 ++#define CB_MEM_TABLE 16 ++ ++struct cb_memory { ++ u32 tag; ++ u32 size; ++ struct cb_memory_range map[0]; ++}; ++ ++#define CB_TAG_HWRPB 0x0002 ++ ++struct cb_hwrpb { ++ u32 tag; ++ u32 size; ++ u64 hwrpb; ++}; ++ ++#define CB_TAG_MAINBOARD 0x0003 ++ ++struct cb_mainboard { ++ u32 tag; ++ u32 size; ++ u8 vendor_idx; ++ u8 part_number_idx; ++ u8 strings[0]; ++}; ++ ++#define CB_TAG_VERSION 0x0004 ++#define CB_TAG_EXTRA_VERSION 0x0005 ++#define CB_TAG_BUILD 0x0006 ++#define CB_TAG_COMPILE_TIME 0x0007 ++#define CB_TAG_COMPILE_BY 0x0008 ++#define CB_TAG_COMPILE_HOST 0x0009 ++#define CB_TAG_COMPILE_DOMAIN 0x000a ++#define CB_TAG_COMPILER 0x000b ++#define CB_TAG_LINKER 0x000c ++#define CB_TAG_ASSEMBLER 0x000d ++ ++struct cb_string { ++ u32 tag; ++ u32 size; ++ u8 string[0]; ++}; ++ ++#define CB_TAG_SERIAL 0x000f ++ ++struct cb_serial { ++ u32 tag; ++ u32 size; ++#define CB_SERIAL_TYPE_IO_MAPPED 1 ++#define CB_SERIAL_TYPE_MEMORY_MAPPED 2 ++ u32 type; ++ u32 baseaddr; ++ u32 baud; ++}; ++ ++#define CB_TAG_CONSOLE 0x00010 ++ ++struct cb_console { ++ u32 tag; ++ u32 size; ++ u16 type; ++}; ++ ++#define CB_TAG_CONSOLE_SERIAL8250 0 ++#define CB_TAG_CONSOLE_VGA 1 /* OBSOLETE */ ++#define CB_TAG_CONSOLE_BTEXT 2 /* OBSOLETE */ ++#define CB_TAG_CONSOLE_LOGBUF 3 ++#define CB_TAG_CONSOLE_SROM 4 /* OBSOLETE */ ++#define CB_TAG_CONSOLE_EHCI 5 ++ ++#define CB_TAG_FORWARD 0x00011 ++ ++struct cb_forward { ++ u32 tag; ++ u32 size; ++ u64 forward; ++}; ++ ++#define CB_TAG_FRAMEBUFFER 0x0012 ++struct cb_framebuffer { ++ u32 tag; ++ u32 size; ++ ++ u64 physical_address; ++ u32 x_resolution; ++ u32 y_resolution; ++ u32 bytes_per_line; ++ u8 bits_per_pixel; ++ u8 red_mask_pos; ++ u8 red_mask_size; ++ u8 green_mask_pos; ++ u8 green_mask_size; ++ u8 blue_mask_pos; ++ u8 blue_mask_size; ++ u8 reserved_mask_pos; ++ u8 reserved_mask_size; ++}; ++ ++#define CB_TAG_GPIO 0x0013 ++#define GPIO_MAX_NAME_LENGTH 16 ++struct cb_gpio { ++ u32 port; ++ u32 polarity; ++ u32 value; ++ u8 name[GPIO_MAX_NAME_LENGTH]; ++}; ++ ++struct cb_gpios { ++ u32 tag; ++ u32 size; ++ ++ u32 count; ++ struct cb_gpio gpios[0]; ++}; ++ ++#define CB_TAG_FDT 0x0014 ++struct cb_fdt { ++ uint32_t tag; ++ uint32_t size; /* size of the entire entry */ ++ /* the actual FDT gets placed here */ ++}; ++ ++#define CB_TAG_VDAT 0x0015 ++struct cb_vdat { ++ uint32_t tag; ++ uint32_t size; /* size of the entire entry */ ++ void *vdat_addr; ++ uint32_t vdat_size; ++}; ++ ++#define CB_TAG_TIMESTAMPS 0x0016 ++#define CB_TAG_CBMEM_CONSOLE 0x0017 ++#define CB_TAG_MRC_CACHE 0x0018 ++struct cb_cbmem_tab { ++ uint32_t tag; ++ uint32_t size; ++ void *cbmem_tab; ++}; ++ ++#define CB_TAG_VBNV 0x0019 ++struct cb_vbnv { ++ uint32_t tag; ++ uint32_t size; ++ uint32_t vbnv_start; ++ uint32_t vbnv_size; ++}; ++ ++#define CB_TAG_CMOS_OPTION_TABLE 0x00c8 ++struct cb_cmos_option_table { ++ u32 tag; ++ u32 size; ++ u32 header_length; ++}; ++ ++#define CB_TAG_OPTION 0x00c9 ++#define CMOS_MAX_NAME_LENGTH 32 ++struct cb_cmos_entries { ++ u32 tag; ++ u32 size; ++ u32 bit; ++ u32 length; ++ u32 config; ++ u32 config_id; ++ u8 name[CMOS_MAX_NAME_LENGTH]; ++}; ++ ++ ++#define CB_TAG_OPTION_ENUM 0x00ca ++#define CMOS_MAX_TEXT_LENGTH 32 ++struct cb_cmos_enums { ++ u32 tag; ++ u32 size; ++ u32 config_id; ++ u32 value; ++ u8 text[CMOS_MAX_TEXT_LENGTH]; ++}; ++ ++#define CB_TAG_OPTION_DEFAULTS 0x00cb ++#define CMOS_IMAGE_BUFFER_SIZE 128 ++struct cb_cmos_defaults { ++ u32 tag; ++ u32 size; ++ u32 name_length; ++ u8 name[CMOS_MAX_NAME_LENGTH]; ++ u8 default_set[CMOS_IMAGE_BUFFER_SIZE]; ++}; ++ ++#define CB_TAG_OPTION_CHECKSUM 0x00cc ++#define CHECKSUM_NONE 0 ++#define CHECKSUM_PCBIOS 1 ++struct cb_cmos_checksum { ++ u32 tag; ++ u32 size; ++ u32 range_start; ++ u32 range_end; ++ u32 location; ++ u32 type; ++}; ++ ++/* Helpful macros */ ++ ++#define MEM_RANGE_COUNT(_rec) \ ++ (((_rec)->size - sizeof(*(_rec))) / sizeof((_rec)->map[0])) ++ ++#define MEM_RANGE_PTR(_rec, _idx) \ ++ (((u8 *) (_rec)) + sizeof(*(_rec)) \ ++ + (sizeof((_rec)->map[0]) * (_idx))) ++ ++#define MB_VENDOR_STRING(_mb) \ ++ (((unsigned char *) ((_mb)->strings)) + (_mb)->vendor_idx) ++ ++#define MB_PART_STRING(_mb) \ ++ (((unsigned char *) ((_mb)->strings)) + (_mb)->part_number_idx) ++ ++#define UNPACK_CB64(_in) \ ++ ((((u64) _in.hi) << 32) | _in.lo) ++ ++struct sysinfo_t; ++ ++int get_coreboot_info(struct sysinfo_t *info); ++ ++#define CBMEM_TOC_RESERVED 512 ++#define MAX_CBMEM_ENTRIES 16 ++#define CBMEM_MAGIC 0x434f5245 ++ ++struct cbmem_entry { ++ u32 magic; ++ u32 id; ++ u64 base; ++ u64 size; ++} __packed; ++ ++#define CBMEM_ID_FREESPACE 0x46524545 ++#define CBMEM_ID_GDT 0x4c474454 ++#define CBMEM_ID_ACPI 0x41435049 ++#define CBMEM_ID_CBTABLE 0x43425442 ++#define CBMEM_ID_PIRQ 0x49525154 ++#define CBMEM_ID_MPTABLE 0x534d5054 ++#define CBMEM_ID_RESUME 0x5245534d ++#define CBMEM_ID_RESUME_SCRATCH 0x52455343 ++#define CBMEM_ID_SMBIOS 0x534d4254 ++#define CBMEM_ID_TIMESTAMP 0x54494d45 ++#define CBMEM_ID_MRCDATA 0x4d524344 ++#define CBMEM_ID_CONSOLE 0x434f4e53 ++#define CBMEM_ID_NONE 0x00000000 ++ ++#endif +diff --git a/arch/x86/include/asm/arch-tangier/timestamp.h b/arch/x86/include/asm/arch-tangier/timestamp.h +new file mode 100644 +index 0000000..fcfc1d5 +--- /dev/null ++++ b/arch/x86/include/asm/arch-tangier/timestamp.h +@@ -0,0 +1,59 @@ ++/* ++ * This file is part of the coreboot project. ++ * ++ * Copyright (C) 2011 The ChromiumOS Authors. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA ++ */ ++ ++#ifndef __COREBOOT_TIMESTAMP_H__ ++#define __COREBOOT_TIMESTAMP_H__ ++ ++enum timestamp_id { ++ /* coreboot specific timestamp IDs */ ++ TS_START_ROMSTAGE = 1, ++ TS_BEFORE_INITRAM = 2, ++ TS_AFTER_INITRAM = 3, ++ TS_END_ROMSTAGE = 4, ++ TS_START_COPYRAM = 8, ++ TS_END_COPYRAM = 9, ++ TS_START_RAMSTAGE = 10, ++ TS_DEVICE_ENUMERATE = 30, ++ TS_DEVICE_CONFIGURE = 40, ++ TS_DEVICE_ENABLE = 50, ++ TS_DEVICE_INITIALIZE = 60, ++ TS_DEVICE_DONE = 70, ++ TS_CBMEM_POST = 75, ++ TS_WRITE_TABLES = 80, ++ TS_LOAD_PAYLOAD = 90, ++ TS_ACPI_WAKE_JUMP = 98, ++ TS_SELFBOOT_JUMP = 99, ++ ++ /* U-Boot entry IDs start at 1000 */ ++ TS_U_BOOT_INITTED = 1000, /* This is where u-boot starts */ ++ TS_U_BOOT_START_KERNEL = 1100, /* Right before jumping to kernel. */ ++}; ++ ++void timestamp_init(void); ++void timestamp_add(enum timestamp_id id, uint64_t ts_time); ++void timestamp_add_now(enum timestamp_id id); ++ ++/** ++ * timestamp_add_to_bootstage - Add important coreboot timestamps to bootstage ++ * ++ * @return 0 if ok, -1 if no timestamps were found ++ */ ++int timestamp_add_to_bootstage(void); ++ ++#endif +diff --git a/arch/x86/include/asm/delay.h b/arch/x86/include/asm/delay.h +new file mode 100644 +index 0000000..18682f2 +--- /dev/null ++++ b/arch/x86/include/asm/delay.h +@@ -0,0 +1,6 @@ ++#ifndef _ASM_X86_DELAY_H ++#define _ASM_X86_DELAY_H ++ ++extern void use_tsc_delay(void); ++ ++#endif /* _ASM_X86_DELAY_H */ +diff --git a/arch/x86/include/asm/io.h b/arch/x86/include/asm/io.h +index 86bac90..9530cf7 100644 +--- a/arch/x86/include/asm/io.h ++++ b/arch/x86/include/asm/io.h +@@ -69,6 +69,54 @@ + #define memcpy_fromio(a,b,c) memcpy((a),(b),(c)) + #define memcpy_toio(a,b,c) memcpy((a),(b),(c)) + ++#define out_arch(type,endian,a,v) __raw_write##type(cpu_to_##endian(v), a) ++#define in_arch(type,endian,a) endian##_to_cpu(__raw_read##type(a)) ++ ++#define out_le32(a,v) out_arch(l,le32,a,v) ++#define out_le16(a,v) out_arch(w,le16,a,v) ++ ++#define in_le32(a) in_arch(l,le32,a) ++#define in_le16(a) in_arch(w,le16,a) ++ ++#define out_be32(a,v) out_arch(l,be32,a,v) ++#define out_be16(a,v) out_arch(w,be16,a,v) ++ ++#define in_be32(a) in_arch(l,be32,a) ++#define in_be16(a) in_arch(w,be16,a) ++ ++#define out_8(a,v) __raw_writeb(v, a) ++#define in_8(a) __raw_readb(a) ++ ++#define clrbits(type, addr, clear) \ ++ out_##type((addr), in_##type(addr) & ~(clear)) ++ ++#define setbits(type, addr, set) \ ++ out_##type((addr), in_##type(addr) | (set)) ++ ++#define clrsetbits(type, addr, clear, set) \ ++ out_##type((addr), (in_##type(addr) & ~(clear)) | (set)) ++ ++#define clrbits_be32(addr, clear) clrbits(be32, addr, clear) ++#define setbits_be32(addr, set) setbits(be32, addr, set) ++#define clrsetbits_be32(addr, clear, set) clrsetbits(be32, addr, clear, set) ++ ++#define clrbits_le32(addr, clear) clrbits(le32, addr, clear) ++#define setbits_le32(addr, set) setbits(le32, addr, set) ++#define clrsetbits_le32(addr, clear, set) clrsetbits(le32, addr, clear, set) ++ ++#define clrbits_be16(addr, clear) clrbits(be16, addr, clear) ++#define setbits_be16(addr, set) setbits(be16, addr, set) ++#define clrsetbits_be16(addr, clear, set) clrsetbits(be16, addr, clear, set) ++ ++#define clrbits_le16(addr, clear) clrbits(le16, addr, clear) ++#define setbits_le16(addr, set) setbits(le16, addr, set) ++#define clrsetbits_le16(addr, clear, set) clrsetbits(le16, addr, clear, set) ++ ++#define clrbits_8(addr, clear) clrbits(8, addr, clear) ++#define setbits_8(addr, set) setbits(8, addr, set) ++#define clrsetbits_8(addr, clear, set) clrsetbits(8, addr, clear, set) ++ ++ + /* + * ISA space is 'always mapped' on a typical x86 system, no need to + * explicitly ioremap() it. The fact that the ISA IO space is mapped +diff --git a/arch/x86/include/asm/mpspec.h b/arch/x86/include/asm/mpspec.h +new file mode 100644 +index 0000000..7609db4 +--- /dev/null ++++ b/arch/x86/include/asm/mpspec.h +@@ -0,0 +1,74 @@ ++#ifndef _ASM_X86_MPSPEC_H ++#define _ASM_X86_MPSPEC_H ++ ++/* from apicdef.h */ ++# define MAX_IO_APICS 64 ++# define MAX_LOCAL_APIC 256 ++ ++#define SFI_MTMR_MAX_NUM 8 ++ ++#define MP_PROCESSOR 0 ++#define MP_BUS 1 ++#define MP_IOAPIC 2 ++#define MP_INTSRC 3 ++#define MP_LINTSRC 4 ++ ++#define BITS_PER_BYTE 8 ++ ++#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) ++ ++#define PHYSID_ARRAY_SIZE BITS_TO_LONGS(MAX_LOCAL_APIC) ++ ++#define MAX_MP_BUSSES 260 ++ ++struct physid_mask { ++ unsigned long mask[PHYSID_ARRAY_SIZE]; ++}; ++ ++typedef struct physid_mask physid_mask_t; ++ ++#define PHYSID_MASK_ALL { {[0 ... PHYSID_ARRAY_SIZE-1] = ~0UL} } ++#define PHYSID_MASK_NONE { {[0 ... PHYSID_ARRAY_SIZE-1] = 0UL} } ++ ++#define physids_empty(map) \ ++ bitmap_empty((map).mask, MAX_LOCAL_APIC) ++ ++enum mp_irq_source_types { ++ mp_INT = 0, ++ mp_NMI = 1, ++ mp_SMI = 2, ++ mp_ExtINT = 3 ++}; ++ ++#define MP_APIC_ALL 0xFF ++ ++enum mp_bustype { ++ MP_BUS_ISA = 1, ++ MP_BUS_EISA, ++ MP_BUS_PCI, ++ MP_BUS_MCA, ++}; ++ ++#define MPC_APIC_USABLE 0x01 ++ ++struct mpc_ioapic { ++ unsigned char type; ++ unsigned char apicid; ++ unsigned char apicver; ++ unsigned char flags; ++ unsigned int apicaddr; ++}; ++ ++struct mpc_intsrc { ++ unsigned char type; ++ unsigned char irqtype; ++ unsigned short irqflag; ++ unsigned char srcbus; ++ unsigned char srcbusirq; ++ unsigned char dstapic; ++ unsigned char dstirq; ++}; ++ ++#define MAX_IRQ_SOURCES 256 ++ ++#endif +diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h +index 0a36e17..a03b6da 100644 +--- a/arch/x86/include/asm/msr-index.h ++++ b/arch/x86/include/asm/msr-index.h +@@ -1,12 +1,3 @@ +-/* +- * Taken from the linux kernel file of the same name +- * +- * (C) Copyright 2012 +- * Graeme Russ, +- * +- * SPDX-License-Identifier: GPL-2.0+ +- */ +- + #ifndef _ASM_X86_MSR_INDEX_H + #define _ASM_X86_MSR_INDEX_H + +@@ -44,6 +35,7 @@ + #define MSR_IA32_PERFCTR0 0x000000c1 + #define MSR_IA32_PERFCTR1 0x000000c2 + #define MSR_FSB_FREQ 0x000000cd ++#define MSR_PLATFORM_INFO 0x000000ce + + #define MSR_NHM_SNB_PKG_CST_CFG_CTL 0x000000e2 + #define NHM_C3_AUTO_DEMOTE (1UL << 25) +@@ -65,6 +57,13 @@ + #define MSR_OFFCORE_RSP_0 0x000001a6 + #define MSR_OFFCORE_RSP_1 0x000001a7 + ++#define MSR_LBR_SELECT 0x000001c8 ++#define MSR_LBR_TOS 0x000001c9 ++#define MSR_LBR_NHM_FROM 0x00000680 ++#define MSR_LBR_NHM_TO 0x000006c0 ++#define MSR_LBR_CORE_FROM 0x00000040 ++#define MSR_LBR_CORE_TO 0x00000060 ++ + #define MSR_IA32_PEBS_ENABLE 0x000003f1 + #define MSR_IA32_DS_AREA 0x00000600 + #define MSR_IA32_PERF_CAPABILITIES 0x00000345 +@@ -91,8 +90,8 @@ + #define MSR_IA32_LASTINTTOIP 0x000001de + + /* DEBUGCTLMSR bits (others vary by model): */ +-#define DEBUGCTLMSR_LBR (1UL << 0) +-#define DEBUGCTLMSR_BTF (1UL << 1) ++#define DEBUGCTLMSR_LBR (1UL << 0) /* last branch recording */ ++#define DEBUGCTLMSR_BTF (1UL << 1) /* single-step on branches */ + #define DEBUGCTLMSR_TR (1UL << 6) + #define DEBUGCTLMSR_BTS (1UL << 7) + #define DEBUGCTLMSR_BTINT (1UL << 8) +@@ -127,6 +126,7 @@ + complete list. */ + + #define MSR_AMD64_PATCH_LEVEL 0x0000008b ++#define MSR_AMD64_TSC_RATIO 0xc0000104 + #define MSR_AMD64_NB_CFG 0xc001001f + #define MSR_AMD64_PATCH_LOADER 0xc0010020 + #define MSR_AMD64_OSVW_ID_LENGTH 0xc0010140 +@@ -237,12 +237,19 @@ + #define MSR_IA32_APICBASE_ENABLE (1<<11) + #define MSR_IA32_APICBASE_BASE (0xfffff<<12) + ++#define MSR_IA32_TSCDEADLINE 0x000006e0 ++ + #define MSR_IA32_UCODE_WRITE 0x00000079 + #define MSR_IA32_UCODE_REV 0x0000008b + + #define MSR_IA32_PERF_STATUS 0x00000198 + #define MSR_IA32_PERF_CTL 0x00000199 + ++#define MSR_IA32_POWER_MISC 0x00000120 ++ ++#define ENABLE_ULFM_AUTOCM (1 << 2) ++#define ENABLE_INDP_AUTOCM (1 << 3) ++ + #define MSR_IA32_MPERF 0x000000e7 + #define MSR_IA32_APERF 0x000000e8 + +@@ -267,6 +274,9 @@ + #define MSR_IA32_TEMPERATURE_TARGET 0x000001a2 + + #define MSR_IA32_ENERGY_PERF_BIAS 0x000001b0 ++#define ENERGY_PERF_BIAS_PERFORMANCE 0 ++#define ENERGY_PERF_BIAS_NORMAL 6 ++#define ENERGY_PERF_BIAS_POWERSAVE 15 + + #define MSR_IA32_PACKAGE_THERM_STATUS 0x000001b1 + +@@ -319,6 +329,8 @@ + #define MSR_IA32_MISC_ENABLE_DCU_PREF_DISABLE (1ULL << 37) + #define MSR_IA32_MISC_ENABLE_TURBO_DISABLE (1ULL << 38) + #define MSR_IA32_MISC_ENABLE_IP_PREF_DISABLE (1ULL << 39) ++#define MSR_IA32_TSC_DEADLINE 0x000006E0 ++ + + /* P4/Xeon+ specific */ + #define MSR_IA32_MCG_EAX 0x00000180 +@@ -446,6 +458,18 @@ + #define MSR_IA32_VMX_VMCS_ENUM 0x0000048a + #define MSR_IA32_VMX_PROCBASED_CTLS2 0x0000048b + #define MSR_IA32_VMX_EPT_VPID_CAP 0x0000048c ++#define MSR_IA32_VMX_TRUE_PINBASED_CTLS 0x0000048d ++#define MSR_IA32_VMX_TRUE_PROCBASED_CTLS 0x0000048e ++#define MSR_IA32_VMX_TRUE_EXIT_CTLS 0x0000048f ++#define MSR_IA32_VMX_TRUE_ENTRY_CTLS 0x00000490 ++ ++/* VMX_BASIC bits and bitmasks */ ++#define VMX_BASIC_VMCS_SIZE_SHIFT 32 ++#define VMX_BASIC_64 0x0001000000000000LLU ++#define VMX_BASIC_MEM_TYPE_SHIFT 50 ++#define VMX_BASIC_MEM_TYPE_MASK 0x003c000000000000LLU ++#define VMX_BASIC_MEM_TYPE_WB 6LLU ++#define VMX_BASIC_INOUT 0x0040000000000000LLU + + /* AMD-V MSRs */ + +diff --git a/arch/x86/include/asm/sfi.h b/arch/x86/include/asm/sfi.h +new file mode 100644 +index 0000000..3d18f6e +--- /dev/null ++++ b/arch/x86/include/asm/sfi.h +@@ -0,0 +1,119 @@ ++/* Copyright (c) 2012 Intel ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of ++ * the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, ++ * MA 02111-1307 USA ++ */ ++ ++#ifndef _SFI_H ++#define _SFI_H ++ ++#include ++#include ++ ++/* Memory type definitions */ ++enum sfi_mem_type { ++ SFI_MEM_RESERVED, ++ SFI_LOADER_CODE, ++ SFI_LOADER_DATA, ++ SFI_BOOT_SERVICE_CODE, ++ SFI_BOOT_SERVICE_DATA, ++ SFI_RUNTIME_SERVICE_CODE, ++ SFI_RUNTIME_SERVICE_DATA, ++ SFI_MEM_CONV, ++ SFI_MEM_UNUSABLE, ++ SFI_ACPI_RECLAIM, ++ SFI_ACPI_NVS, ++ SFI_MEM_MMIO, ++ SFI_MEM_IOPORT, ++ SFI_PAL_CODE, ++ SFI_MEM_TYPEMAX, ++}; ++ ++#define SFI_SYST_MAGIC 0x54535953 ++#define SFI_MMAP_MAGIC 0x50414d4d ++ ++struct sfi_mem_entry { ++ enum sfi_mem_type type; ++ u64 phy_start; ++ u64 vir_start; ++ u64 pages; ++ u64 attrib; ++} __attribute__ ((packed)); ++ ++struct sfi_apic_table_entry { ++ u64 phys_addr; /* phy base addr for APIC reg */ ++} __attribute__ ((packed)); ++ ++struct sfi_timer_table_entry { ++ u64 phys_addr; /* phy base addr for the timer */ ++ u32 freq_hz; /* in HZ */ ++ u32 irq; ++} __attribute__ ((packed)); ++ ++struct sfi_table_header { ++ char signature[4]; ++ u32 length; ++ u8 revision; ++ u8 checksum; ++ char oem_id[6]; ++ char oem_table_id[8]; ++} __attribute__ ((packed)); ++ ++struct sfi_quad_word { ++ u32 low; ++ u32 high; ++}; ++ ++struct sfi_table { ++ struct sfi_table_header header; ++ union { ++ u64 pentry[1]; ++ struct sfi_quad_word entry[1]; ++ }; ++} __attribute__ ((packed)); ++ ++#define SFI_TBL_HEADER_LEN 24 ++ ++#define SFI_GET_NUM_ENTRIES(ptable, entry_type) \ ++ ((ptable->header.length - sizeof(struct sfi_table_header)) / \ ++ (sizeof(entry_type))) ++ ++#define SFI_GET_ENTRY_NUM(ptable, entry) \ ++ ((ptable->header.length - SFI_TBL_HEADER_LEN) / \ ++ (sizeof(struct entry))) ++ ++#define SFI_BASE_ADDR 0x000E0000 ++#define SFI_LENGTH 0x00020000 ++ ++extern int sfi_mtimer_num; ++ ++void sfi_parse_mtmr(void); ++struct sfi_timer_table_entry *sfi_get_mtmr(int hint); ++void fill_memranges_from_e820(struct sysinfo_t *info); ++ ++#ifdef CONFIG_X86_IO_APIC ++int sfi_parse_ioapic(void); ++#endif /* CONFIG_X86_IO_APIC */ ++ ++#ifdef CONFIG_DETECT_RAM_SIZE_WORKAROUND ++unsigned long long sfi_get_max_usable_ram(struct sysinfo_t *info); ++#endif ++ ++#ifdef CONFIG_SFI ++extern phys_size_t sfi_get_ram_size(void); ++extern unsigned sfi_setup_e820(unsigned max_entries, struct e820entry *entries); ++#endif ++ ++#endif /* _SFI_H */ +diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile +index f7303ab..85274ab 100644 +--- a/arch/x86/lib/Makefile ++++ b/arch/x86/lib/Makefile +@@ -19,6 +19,7 @@ obj-y += string.o + obj-$(CONFIG_SYS_X86_TSC_TIMER) += tsc_timer.o + obj-$(CONFIG_VIDEO_VGA) += video.o + obj-$(CONFIG_CMD_ZBOOT) += zimage.o ++obj-$(CONFIG_SFI) += sfi.o + + LIBGCC := $(notdir $(NORMAL_LIBGCC)) + extra-y := $(LIBGCC) +diff --git a/arch/x86/lib/sfi.c b/arch/x86/lib/sfi.c +new file mode 100644 +index 0000000..675ad88 +--- /dev/null ++++ b/arch/x86/lib/sfi.c +@@ -0,0 +1,162 @@ ++#include ++#include ++#include ++ ++#define SFI_BASE_ADDR 0x000E0000 ++#define SFI_LENGTH 0x00020000 ++#define SFI_TABLE_LENGTH 16 ++ ++static int sfi_table_check(struct sfi_table_header *sbh) ++{ ++ char chksum = 0; ++ char *pos = (char *)sbh; ++ int i; ++ ++ if (sbh->length < SFI_TABLE_LENGTH) ++ return -1; ++ ++ if (sbh->length > SFI_LENGTH) ++ return -1; ++ ++ for (i = 0; i < sbh->length; i++) ++ chksum += *pos++; ++ ++ if (chksum) ++ error("sfi: Invalid checksum\n"); ++ ++ /* checksum is ok if zero */ ++ return chksum; ++} ++ ++static unsigned long sfi_search_mmap(void) ++{ ++ u32 i = 0; ++ u32 *pos = (u32 *) SFI_BASE_ADDR; ++ u32 *end = (u32 *) (SFI_BASE_ADDR + SFI_LENGTH); ++ struct sfi_table_header *sbh; ++ struct sfi_table *sb; ++ u32 sys_entry_cnt = 0; ++ ++ /* Find SYST table */ ++ for (; pos < end; pos += 4) { ++ if (*pos == SFI_SYST_MAGIC) { ++ if (!sfi_table_check((struct sfi_table_header *)pos)) ++ break; ++ } ++ } ++ ++ if (pos >= end) { ++ error("failed to locate SFI SYST table\n"); ++ return 0; ++ } ++ ++ /* map table pointers */ ++ sb = (struct sfi_table *)pos; ++ sbh = (struct sfi_table_header *)sb; ++ ++ sys_entry_cnt = (sbh->length - sizeof(struct sfi_table_header)) >> 3; ++ ++ /* Search through each SYST entry for MMAP table */ ++ for (i = 0; i < sys_entry_cnt; i++) { ++ sbh = (struct sfi_table_header *)sb->entry[i].low; ++ if (*(u32 *) sbh->signature == SFI_MMAP_MAGIC) { ++ if (!sfi_table_check((struct sfi_table_header *)sbh)) ++ return (unsigned long)sbh; ++ } ++ } ++ ++ return 0; ++} ++ ++unsigned sfi_setup_e820(unsigned max_entries, struct e820entry *entries) ++{ ++ struct sfi_table *sb; ++ struct sfi_mem_entry *mentry; ++ unsigned long long start, end, size; ++ int i, num, type, total; ++ ++ total = 0; ++ ++ /* search for sfi mmap table */ ++ sb = (struct sfi_table *)sfi_search_mmap(); ++ if (!sb) { ++ error("failed to locate SFI MMAP table\n"); ++ return 0; ++ } ++ debug("will use sfi mmap table for e820 table\n"); ++ num = SFI_GET_ENTRY_NUM(sb, sfi_mem_entry); ++ mentry = (struct sfi_mem_entry *)sb->pentry; ++ ++ for (i = 0; i < num; i++) { ++ start = mentry->phy_start; ++ size = mentry->pages << 12; ++ end = start + size; ++ ++ if (start > end) ++ continue; ++ ++ /* translate SFI mmap type to E820 map type */ ++ switch (mentry->type) { ++ case SFI_MEM_CONV: ++ type = E820_RAM; ++ break; ++ case SFI_MEM_UNUSABLE: ++ case SFI_RUNTIME_SERVICE_DATA: ++ mentry++; ++ continue; ++ default: ++ type = E820_RESERVED; ++ } ++ ++ if (total == E820MAX) ++ break; ++ entries[total].addr = start; ++ entries[total].size = size; ++ entries[total++].type = type; ++ ++ mentry++; ++ } ++ ++ return total; ++} ++ ++phys_size_t sfi_get_ram_size(void) ++{ ++ struct sfi_table *sb; ++ struct sfi_mem_entry *mentry; ++ unsigned long long start, end, size; ++ int i, num; ++ phys_size_t ram = 0; ++ ++ /* search for sfi mmap table */ ++ sb = (struct sfi_table *)sfi_search_mmap(); ++ if (!sb) { ++ error("failed to locate SFI MMAP table\n"); ++ return 0; ++ } ++ debug("will use sfi mmap table for e820 table\n"); ++ num = SFI_GET_ENTRY_NUM(sb, sfi_mem_entry); ++ mentry = (struct sfi_mem_entry *)sb->pentry; ++ ++ for (i = 0; i < num; i++, mentry++) { ++ if (mentry->type != SFI_MEM_CONV) ++ continue; ++ ++ start = mentry->phy_start; ++ size = mentry->pages << 12; ++ end = start + size; ++ ++ if (start > end) ++ continue; ++ ++ if (ram < end) ++ ram = end; ++ } ++ ++ /* round up to 512mb */ ++ ram = (ram + (512 * 1024 * 1024 - 1)) & ~(512 * 1024 * 1024 - 1); ++ ++ debug("ram size %llu\n", ram); ++ ++ return ram; ++} +diff --git a/arch/x86/lib/tsc_timer.c b/arch/x86/lib/tsc_timer.c +index 8b38702..bd751ed 100644 +--- a/arch/x86/lib/tsc_timer.c ++++ b/arch/x86/lib/tsc_timer.c +@@ -34,13 +34,13 @@ u64 __attribute__((no_instrument_function)) get_ticks(void) + return now_tick - gd->arch.tsc_base; + } + +-#define PLATFORM_INFO_MSR 0xce ++ + + /* Get the speed of the TSC timer in MHz */ +-unsigned __attribute__((no_instrument_function)) long get_tbclk_mhz(void) ++unsigned __weak __attribute__((no_instrument_function)) long get_tbclk_mhz(void) + { + u32 ratio; +- u64 platform_info = native_read_msr(PLATFORM_INFO_MSR); ++ u64 platform_info = native_read_msr(MSR_PLATFORM_INFO); + + /* 100MHz times Max Non Turbo ratio */ + ratio = (platform_info >> 8) & 0xff; +diff --git a/arch/x86/lib/zimage.c b/arch/x86/lib/zimage.c +index 1dab3cc..e1387bb 100644 +--- a/arch/x86/lib/zimage.c ++++ b/arch/x86/lib/zimage.c +@@ -252,6 +252,10 @@ int setup_zimage(struct boot_params *setup_base, char *cmd_line, int auto_boot, + hdr->setup_move_size = 0x9100; + } + ++#ifdef CONFIG_INTEL_MID ++ hdr->hardware_subarch = X86_SUBARCH_MRST; ++#endif ++ + /* build command line at COMMAND_LINE_OFFSET */ + build_command_line(cmd_line, auto_boot); + return 0; +diff --git a/board/intel/edison/Makefile b/board/intel/edison/Makefile +new file mode 100644 +index 0000000..a29f512 +--- /dev/null ++++ b/board/intel/edison/Makefile +@@ -0,0 +1 @@ ++obj-y += edison_start.o edison.o +diff --git a/board/intel/edison/config.mk b/board/intel/edison/config.mk +new file mode 100644 +index 0000000..0c05dd0 +--- /dev/null ++++ b/board/intel/edison/config.mk +@@ -0,0 +1,7 @@ ++# ++# Copyright (c) 2011 The Chromium OS Authors. All rights reserved. ++# ++# SPDX-License-Identifier: GPL-2.0 BSD-3-Clause ++# ++ ++HOSTCFLAGS_autoconf.mk.dep = -Wno-variadic-macros +diff --git a/board/intel/edison/edison.c b/board/intel/edison/edison.c +new file mode 100644 +index 0000000..8d8a6e6 +--- /dev/null ++++ b/board/intel/edison/edison.c +@@ -0,0 +1,54 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++DECLARE_GLOBAL_DATA_PTR; ++ ++#ifndef CONFIG_WATCHDOG_HEARTBEAT ++#define WATCHDOG_HEARTBEAT 30 ++#else ++#define WATCHDOG_HEARTBEAT CONFIG_WATCHDOG_HEARTBEAT ++#endif ++ ++enum { ++ SCU_WATCHDOG_START = 0, ++ SCU_WATCHDOG_STOP, ++ SCU_WATCHDOG_KEEPALIVE, ++ SCU_WATCHDOG_SET_ACTION_ON_TIMEOUT ++}; ++ ++#define IPC_CMD(cmd, sub) (sub << 12 | cmd) ++ ++int board_usb_init(int index, enum usb_init_type init) ++{ ++ return usb_gadget_init_udc(); ++} ++ ++void watchdog_reset(void) ++{ ++ ulong now = timer_get_us(); ++ ++ /* do not flood SCU */ ++ if (unlikely((now - gd->arch.tsc_prev) > (WATCHDOG_HEARTBEAT * 1000000))) ++ { ++ gd->arch.tsc_prev = now; ++ intel_scu_ipc_send_command(IPC_CMD(IPCMSG_WATCHDOG_TIMER, ++ SCU_WATCHDOG_KEEPALIVE)); ++ } ++} ++ ++int watchdog_disable(void) ++{ ++ return (intel_scu_ipc_simple_command(IPCMSG_WATCHDOG_TIMER, ++ SCU_WATCHDOG_STOP)); ++} ++ ++int watchdog_init(void) ++{ ++ return (intel_scu_ipc_simple_command(IPCMSG_WATCHDOG_TIMER, ++ SCU_WATCHDOG_START)); ++} +diff --git a/board/intel/edison/edison.h b/board/intel/edison/edison.h +new file mode 100644 +index 0000000..e69de29 +diff --git a/board/intel/edison/edison_start.S b/board/intel/edison/edison_start.S +new file mode 100644 +index 0000000..932fe6c +--- /dev/null ++++ b/board/intel/edison/edison_start.S +@@ -0,0 +1,13 @@ ++/* ++ * Copyright (c) 2011 The Chromium OS Authors. ++ * (C) Copyright 2008 ++ * Graeme Russ, graeme.russ@gmail.com. ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++/* board early intialization */ ++.globl early_board_init ++early_board_init: ++ /* No 32-bit board specific initialisation */ ++ jmp early_board_init_ret +diff --git a/boards.cfg b/boards.cfg +index b4203f1..bf4c268 100644 +--- a/boards.cfg ++++ b/boards.cfg +@@ -1191,6 +1191,7 @@ Active sparc leon3 - gaisler - + Active sparc leon3 - gaisler - gr_xc3s_1500 - - + Active sparc leon3 - gaisler - grsim - - + Active x86 x86 coreboot chromebook-x86 coreboot coreboot-x86 coreboot:SYS_TEXT_BASE=0x01110000 - ++Active x86 sivermont tangier intel edison edison edison:SYS_USB_OTG_BASE=0xf9100000,SYS_EMMC_PORT_BASE=0xff3fc000,SYS_TEXT_BASE=0x1101000 - + # The following were moved to "Orphan" in March, 2014 + Orphan blackfin blackfin - - - cm-bf527 - Bluetechnix Tinyboards + Orphan blackfin blackfin - - - cm-bf533 - Bluetechnix Tinyboards +diff --git a/common/Makefile b/common/Makefile +index cecd81a..3c65100 100644 +--- a/common/Makefile ++++ b/common/Makefile +@@ -175,6 +175,8 @@ obj-$(CONFIG_CMD_SPL) += cmd_spl.o + obj-$(CONFIG_CMD_ZIP) += cmd_zip.o + obj-$(CONFIG_CMD_ZFS) += cmd_zfs.o + ++COBJS-$(CONFIG_CMD_FASTBOOT) += cmd_fastboot.o ++ + # others + obj-$(CONFIG_BOOTSTAGE) += bootstage.o + obj-$(CONFIG_CONSOLE_MUX) += iomux.o +diff --git a/common/board_f.c b/common/board_f.c +index f285bad..52b715c 100644 +--- a/common/board_f.c ++++ b/common/board_f.c +@@ -280,7 +280,7 @@ __weak int arch_cpu_init(void) + return 0; + } + +-#ifdef CONFIG_OF_HOSTFILE ++#if defined(CONFIG_USE_FDT) && defined(CONFIG_OF_HOSTFILE) + + static int read_fdt_from_file(void) + { +@@ -335,6 +335,7 @@ static int setup_ram_buf(void) + } + #endif + ++#ifdef CONFIG_USE_FDT + static int setup_fdt(void) + { + #ifdef CONFIG_OF_EMBED +@@ -354,6 +355,7 @@ static int setup_fdt(void) + (uintptr_t)gd->fdt_blob); + return 0; + } ++#endif + + /* Get the top of usable RAM */ + __weak ulong board_get_usable_ram_top(ulong total_size) +@@ -549,6 +551,7 @@ static int reserve_global_data(void) + return 0; + } + ++#ifdef CONFIG_USE_FDT + static int reserve_fdt(void) + { + /* +@@ -567,6 +570,7 @@ static int reserve_fdt(void) + + return 0; + } ++#endif + + static int reserve_stacks(void) + { +@@ -724,6 +728,7 @@ static int setup_dram_config(void) + return 0; + } + ++#ifdef CONFIG_USE_FDT + static int reloc_fdt(void) + { + if (gd->new_fdt) { +@@ -733,6 +738,7 @@ static int reloc_fdt(void) + + return 0; + } ++#endif + + static int setup_reloc(void) + { +@@ -789,7 +795,9 @@ static init_fnc_t init_sequence_f[] = { + setup_ram_buf, + #endif + setup_mon_len, ++#ifdef CONFIG_USE_FDT + setup_fdt, ++#endif + trace_early_init, + #if defined(CONFIG_MPC85xx) || defined(CONFIG_MPC86xx) + /* TODO: can this go into arch_cpu_init()? */ +@@ -798,12 +806,12 @@ static init_fnc_t init_sequence_f[] = { + arch_cpu_init, /* basic arch cpu dependent setup */ + #ifdef CONFIG_X86 + cpu_init_f, /* TODO(sjg@chromium.org): remove */ +-# ifdef CONFIG_OF_CONTROL ++#if defined(CONFIG_USE_FDT) && defined(CONFIG_OF_CONTROL) + find_fdt, /* TODO(sjg@chromium.org): remove */ + # endif + #endif + mark_bootstage, +-#ifdef CONFIG_OF_CONTROL ++#if defined(CONFIG_USE_FDT) && defined(CONFIG_OF_CONTROL) + fdtdec_check_fdt, + #endif + #if defined(CONFIG_BOARD_EARLY_INIT_F) +@@ -847,7 +855,7 @@ static init_fnc_t init_sequence_f[] = { + #ifdef CONFIG_SANDBOX + sandbox_early_getopt_check, + #endif +-#ifdef CONFIG_OF_CONTROL ++#if defined(CONFIG_USE_FDT) && defined(CONFIG_OF_CONTROL) + fdtdec_prepare_fdt, + #endif + display_options, /* say that we are here */ +@@ -945,7 +953,9 @@ static init_fnc_t init_sequence_f[] = { + #endif + setup_machine, + reserve_global_data, ++#ifdef CONFIG_USE_FDT + reserve_fdt, ++#endif + reserve_stacks, + setup_dram_config, + show_dram_config, +@@ -960,7 +970,9 @@ static init_fnc_t init_sequence_f[] = { + setup_board_extra, + #endif + INIT_FUNC_WATCHDOG_RESET ++#ifdef CONFIG_USE_FDT + reloc_fdt, ++#endif + setup_reloc, + #if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) + jump_to_copy, +diff --git a/common/cmd_dfu.c b/common/cmd_dfu.c +index 5547678..ac7db5e 100644 +--- a/common/cmd_dfu.c ++++ b/common/cmd_dfu.c +@@ -21,7 +21,9 @@ static int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) + char *usb_controller = argv[1]; + char *interface = argv[2]; + char *devstring = argv[3]; +- ++#ifdef CONFIG_DFU_TIMEOUT ++ ulong dfu_timeout = 0 * 1000; ++#endif + char *s = "dfu"; + int ret, i = 0; + +@@ -34,11 +36,20 @@ static int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) + dfu_show_entities(); + goto done; + } ++#ifdef CONFIG_DFU_TIMEOUT ++ if (argc > 4) ++ dfu_timeout = simple_strtoul(argv[4], NULL, 0) * 1000 ; ++#endif + + int controller_index = simple_strtoul(usb_controller, NULL, 0); + board_usb_init(controller_index, USB_INIT_DEVICE); +- + g_dnl_register(s); ++ ++#ifdef CONFIG_DFU_TIMEOUT ++ ulong time_activity_start = get_timer(0); ++ ulong time_inactivity_start = time_activity_start + dfu_timeout; ++ ulong next_print_time = 0; ++#endif + while (1) { + if (dfu_reset()) + /* +@@ -48,10 +59,32 @@ static int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) + */ + if (++i == 10) + goto exit; +- ++#ifdef CONFIG_DFU_TIMEOUT ++ if (dfu_enum_done() && (time_inactivity_start != time_activity_start)) ++ { ++ time_inactivity_start = time_activity_start; ++ debug("\nDFU connection established\n"); ++ } ++#endif + if (ctrlc()) + goto exit; ++#ifdef CONFIG_DFU_TIMEOUT ++ if (time_activity_start != time_inactivity_start) ++ { ++ ulong cur_time = get_timer(time_activity_start); + ++ if ( cur_time > dfu_timeout ) ++ { ++ debug("\nInactivity Timeout, Abort Dfu\n"); ++ goto exit; ++ } ++ if ( next_print_time == 0 || (get_timer(next_print_time)> 800)) ++ { ++ debug("\rAborting in %lu sec", (dfu_timeout - cur_time)/1000); ++ next_print_time = get_timer(0); ++ } ++ } ++#endif + usb_gadget_handle_interrupts(); + } + exit: +@@ -67,9 +100,16 @@ done: + + U_BOOT_CMD(dfu, CONFIG_SYS_MAXARGS, 1, do_dfu, + "Device Firmware Upgrade", ++#ifdef CONFIG_DFU_TIMEOUT ++ " [list|timeout]\n" ++#else + " [list]\n" ++#endif + " - device firmware upgrade via \n" + " on device , attached to interface\n" + " \n" + " [list] - list available alt settings\n" ++#ifdef CONFIG_DFU_TIMEOUT ++ " [timeout] - specify inactivity timeout in sec, doesn't work whit list" ++#endif + ); +diff --git a/common/cmd_fastboot.c b/common/cmd_fastboot.c +new file mode 100644 +index 0000000..9a656dd +--- /dev/null ++++ b/common/cmd_fastboot.c +@@ -0,0 +1,28 @@ ++#include ++#include ++#include ++ ++static int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ int ret = 1; ++ ++ if (!fastboot_init()) { ++ printf("Fastboot entered...\n"); ++ ++ ret = 0; ++ ++ while (1) { ++ if (fastboot_poll()) ++ break; ++ } ++ } ++ ++ fastboot_shutdown(); ++ return ret; ++} ++ ++U_BOOT_CMD( ++ fastboot, 1, 1, do_fastboot, ++ "fastboot- use USB Fastboot protocol\n", ++ "" ++); +diff --git a/common/cmd_gpt.c b/common/cmd_gpt.c +index e38422d..c8317b4 100644 +--- a/common/cmd_gpt.c ++++ b/common/cmd_gpt.c +@@ -54,7 +54,7 @@ static int extract_env(const char *str, char **env) + if (e == NULL) { + #ifdef CONFIG_RANDOM_UUID + debug("%s unset. ", str); +- gen_rand_uuid_str(uuid_str, UUID_STR_FORMAT_STD); ++ gen_rand_uuid_str(uuid_str, UUID_STR_FORMAT_GUID); + setenv(s, uuid_str); + + e = getenv(s); +diff --git a/common/cmd_itest.c b/common/cmd_itest.c +index ae2527b..a965b15 100644 +--- a/common/cmd_itest.c ++++ b/common/cmd_itest.c +@@ -63,7 +63,8 @@ static long evalexp(char *s, int w) + l = simple_strtoul(s, NULL, 16); + } + +- return (l & ((1 << (w * 8)) - 1)); ++ /* avoid overflow on mask calculus */ ++ return ((sizeof(long) <= w )? l : (l & ((1 << (w * 8)) - 1))); + } + + static char * evalstr(char *s) +diff --git a/common/cmd_part.c b/common/cmd_part.c +index 1424854..79c0297 100644 +--- a/common/cmd_part.c ++++ b/common/cmd_part.c +@@ -21,11 +21,127 @@ + #include + #include + #include ++#include + + #ifndef CONFIG_PARTITION_UUIDS + #error CONFIG_PARTITION_UUIDS must be enabled for CONFIG_CMD_PART to be enabled + #endif + ++int do_part_info(int argc, char * const argv[]) ++{ ++ int part; ++ block_dev_desc_t *dev_desc; ++ disk_partition_t info; ++ char buf_convert[20]; ++ if (argc < 2) ++ return CMD_RET_USAGE; ++ if (argc > 5) ++ return CMD_RET_USAGE; ++ ++ part = get_device_and_partition(argv[0], argv[1], &dev_desc, &info, 0); ++ if (part < 0) ++ return 1; ++ ++ snprintf(buf_convert, sizeof(buf_convert), LBAF, info.start); ++ if (argc > 2) ++ setenv(argv[2], buf_convert); ++ else ++ printf("Partition start :0x%s\n", buf_convert); ++ ++ snprintf(buf_convert, sizeof(buf_convert), LBAF, info.size); ++ if (argc > 3) ++ setenv(argv[3], buf_convert); ++ else ++ printf("Partition size :0x%s\n", buf_convert); ++ ++ snprintf(buf_convert, sizeof(buf_convert), "%lx", info.blksz); ++ if (argc > 4) ++ setenv(argv[4], buf_convert); ++ else ++ printf("Block size :0x%s bytes\n", buf_convert); ++ return 0; ++} ++ ++#define MAX_SEARCH_PARTITIONS 128 ++int do_part_find(int argc, char * const argv[]) ++{ ++ int part, dev, i, ret=1; ++ block_dev_desc_t *dev_desc; ++ disk_partition_t info; ++ size_t mnb_of; ++ const char *value_str; ++ char *dup_str = NULL; ++ const char *criteria_str; ++ char buf_str[4]; ++ ++ if (argc < 3) ++ return CMD_RET_USAGE; ++ if (argc > 4) ++ return CMD_RET_USAGE; ++ ++ dev = get_device(argv[0], argv[1], &dev_desc); ++ if (dev < 0) ++ return 1; ++ ++ /* check and split criteria:value */ ++ value_str = strchr(argv[2],':'); ++ if (!value_str) ++ return CMD_RET_USAGE; ++ ++ dup_str = strdup(argv[2]); ++ if(!dup_str) ++ return 1; ++ ++ if ( (value_str - argv[2]) > strlen(dup_str) ) ++ return 1; ++ ++ dup_str[value_str - argv[2]] = 0; ++ criteria_str = dup_str; ++ value_str++; ++ ++ if(!strcmp(criteria_str, "uuid")) ++ mnb_of = offsetof(disk_partition_t, uuid); ++ else if(!strcmp(criteria_str, "label")) ++ mnb_of = offsetof(disk_partition_t, name); ++ else { ++ printf("Bad criteria: %s\n", criteria_str); ++ ret = CMD_RET_USAGE; ++ goto end_of_part_find; ++ } ++ ++ part=-1; ++ for (i = 1; i <= MAX_SEARCH_PARTITIONS; i++) { ++ ret = get_partition_info(dev_desc, i, &info); ++ if (ret) ++ continue; ++ ++ if(!strcmp(((char*)&info + mnb_of), value_str)) { ++ part = i; ++ ret = 0; ++ break; ++ } ++ } ++ ++ if ( part == -1) { ++ printf("No partition found\n"); ++ ret = 1; ++ goto end_of_part_find; ++ } ++ ++ snprintf(buf_str, sizeof(buf_str), "%d", part); ++ if ( argc > 3) ++ setenv(argv[3],buf_str); ++ else ++ printf("Partition %s correspond to %s == %s\n", ++ buf_str, criteria_str, value_str); ++ ++end_of_part_find: ++ if( dup_str ) ++ free (dup_str); ++ ++ return ret; ++} ++ + int do_part_uuid(int argc, char * const argv[]) + { + int part; +@@ -75,17 +191,30 @@ int do_part(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) + return do_part_uuid(argc - 2, argv + 2); + else if (!strcmp(argv[1], "list")) + return do_part_list(argc - 2, argv + 2); +- ++ else if (!strcmp(argv[1], "info")) ++ return do_part_info(argc - 2, argv + 2); ++ else if (!strcmp(argv[1], "find")) ++ return do_part_find(argc - 2, argv + 2); + return CMD_RET_USAGE; + } + + U_BOOT_CMD( +- part, 5, 1, do_part, ++ part, 7, 1, do_part, + "disk partition related commands", ++ "find :\n" ++ " - print first partition number corresponding to criteria:valuei\n" ++ " - criteria could be label or uuid\n" ++ "part find : \n" ++ " - set environment variable to first partition number corresponding to criteria:valuei\n" ++ " - criteria could be label or uuid\n" + "uuid :\n" + " - print partition UUID\n" + "part uuid : \n" + " - set environment variable to partition UUID\n" + "part list \n" +- " - print a device's partition table" ++ " - print a device's partition table\n" ++ "part info : \n" ++ " - set environment variable varname-start to partition start in blocks\n" ++ " - set environment variable varname-size to partition size in blocks\n" ++ " - set environment variable varname-blcksize to partition block size in bytes\n" + ); +diff --git a/disk/part_efi.c b/disk/part_efi.c +index 216a292..4b32049 100644 +--- a/disk/part_efi.c ++++ b/disk/part_efi.c +@@ -201,10 +201,12 @@ static int set_protective_mbr(block_dev_desc_t *dev_desc) + ALLOC_CACHE_ALIGN_BUFFER(legacy_mbr, p_mbr, 1); + memset(p_mbr, 0, sizeof(*p_mbr)); + +- if (p_mbr == NULL) { +- printf("%s: calloc failed!\n", __func__); ++ /* Read MBR to backup boot_code if it exists */ ++ if (dev_desc->block_read(dev_desc->dev, 0, 1, p_mbr) != 1) { ++ error("** Can't read from device %d **\n", dev_desc->dev); + return -1; + } ++ + /* Append signature */ + p_mbr->signature = MSDOS_MBR_SIGNATURE; + p_mbr->partition_record[0].sys_ind = EFI_PMBR_OSTYPE_EFI_GPT; +@@ -324,7 +326,7 @@ int gpt_fill_pte(gpt_header *gpt_h, gpt_entry *gpt_e, + str_uuid = partitions[i].uuid; + bin_uuid = gpt_e[i].unique_partition_guid.b; + +- if (uuid_str_to_bin(str_uuid, bin_uuid, UUID_STR_FORMAT_STD)) { ++ if (uuid_str_to_bin(str_uuid, bin_uuid, UUID_STR_FORMAT_GUID)) { + printf("Partition no. %d: invalid guid: %s\n", + i, str_uuid); + return -1; +diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c +index 8a09aaf..fa4bfc3 100644 +--- a/drivers/dfu/dfu.c ++++ b/drivers/dfu/dfu.c +@@ -17,6 +17,7 @@ + #include + + static bool dfu_reset_request; ++static bool dfu_enum_request; + static LIST_HEAD(dfu_list); + static int dfu_alt_num; + static int alt_num_cnt; +@@ -31,6 +32,16 @@ void dfu_trigger_reset() + dfu_reset_request = true; + } + ++bool dfu_enum_done(void) ++{ ++ return dfu_enum_request; ++} ++ ++void dfu_trigger_enum_done() ++{ ++ dfu_enum_request = true; ++} ++ + static int dfu_find_alt_num(const char *s) + { + int i = 0; +@@ -61,6 +72,7 @@ int dfu_init_env_entities(char *interface, int dev) + return ret; + } + ++ dfu_enum_request = false; + free(env_bkp); + return 0; + } +diff --git a/drivers/dfu/dfu_mmc.c b/drivers/dfu/dfu_mmc.c +index 651cfff..a7224b6 100644 +--- a/drivers/dfu/dfu_mmc.c ++++ b/drivers/dfu/dfu_mmc.c +@@ -18,11 +18,29 @@ static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE) + dfu_file_buf[CONFIG_SYS_DFU_MAX_FILE_SIZE]; + static long dfu_file_buf_len; + ++static int mmc_access_part(struct dfu_entity *dfu, struct mmc *mmc, int part) ++{ ++ int ret; ++ ++ if (part == mmc->part_num) ++ return 0; ++ ++ ret = mmc_switch_part(dfu->dev_num, part); ++ if (ret) { ++ error("Cannot switch to partition %d\n", part); ++ return ret; ++ } ++ mmc->part_num = part; ++ ++ return 0; ++} ++ + static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu, + u64 offset, void *buf, long *len) + { + struct mmc *mmc = find_mmc_device(dfu->dev_num); + u32 blk_start, blk_count, n = 0; ++ int ret, part_num_bkp = 0; + + /* + * We must ensure that we work in lba_blk_size chunks, so ALIGN +@@ -39,6 +57,13 @@ static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu, + return -EINVAL; + } + ++ if (dfu->data.mmc.partition_access != DFU_NOT_SUPPORTED) { ++ part_num_bkp = mmc->part_num; ++ ret = mmc_access_part(dfu, mmc, dfu->data.mmc.partition_access); ++ if (ret) ++ return ret; ++ } ++ + debug("%s: %s dev: %d start: %d cnt: %d buf: 0x%p\n", __func__, + op == DFU_OP_READ ? "MMC READ" : "MMC WRITE", dfu->dev_num, + blk_start, blk_count, buf); +@@ -57,9 +82,17 @@ static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu, + + if (n != blk_count) { + error("MMC operation failed"); ++ if (dfu->data.mmc.partition_access != DFU_NOT_SUPPORTED) ++ mmc_access_part(dfu, mmc, part_num_bkp); + return -EIO; + } + ++ if (dfu->data.mmc.partition_access != DFU_NOT_SUPPORTED) { ++ ret = mmc_access_part(dfu, mmc, part_num_bkp); ++ if (ret) ++ return ret; ++ } ++ + return 0; + } + +@@ -193,12 +226,22 @@ int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s) + char *st; + + dfu->dev_type = DFU_DEV_MMC; ++ dfu->data.mmc.partition_access = DFU_NOT_SUPPORTED; ++ + st = strsep(&s, " "); + if (!strcmp(st, "mmc")) { + dfu->layout = DFU_RAW_ADDR; + dfu->data.mmc.lba_start = simple_strtoul(s, &s, 16); +- dfu->data.mmc.lba_size = simple_strtoul(++s, &s, 16); ++ s++; ++ dfu->data.mmc.lba_size = simple_strtoul(s, &s, 16); + dfu->data.mmc.lba_blk_size = get_mmc_blk_size(dfu->dev_num); ++ if (*s) { ++ s++; ++ st = strsep(&s, " "); ++ if (!strcmp(st, "mmcpart")) ++ dfu->data.mmc.partition_access = ++ simple_strtoul(s, &s, 0); ++ } + } else if (!strcmp(st, "fat")) { + dfu->layout = DFU_FS_FAT; + } else if (!strcmp(st, "ext4")) { +@@ -236,7 +279,8 @@ int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s) + + if (dfu->layout == DFU_FS_EXT4 || dfu->layout == DFU_FS_FAT) { + dfu->data.mmc.dev = simple_strtoul(s, &s, 10); +- dfu->data.mmc.part = simple_strtoul(++s, &s, 10); ++ s++; ++ dfu->data.mmc.part = simple_strtoul(s, &s, 10); + } + + dfu->read_medium = dfu_read_medium_mmc; +diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile +index 2f2e48f..889fe05 100644 +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -23,3 +23,4 @@ obj-$(CONFIG_PDSP188x) += pdsp188x.o + obj-$(CONFIG_STATUS_LED) += status_led.o + obj-$(CONFIG_TWL4030_LED) += twl4030_led.o + obj-$(CONFIG_FSL_IFC) += fsl_ifc.o ++obj-$(CONFIG_INTEL_SCU) += intel_scu_ipc.o +diff --git a/drivers/misc/intel_scu_ipc.c b/drivers/misc/intel_scu_ipc.c +new file mode 100644 +index 0000000..5843fc8 +--- /dev/null ++++ b/drivers/misc/intel_scu_ipc.c +@@ -0,0 +1,150 @@ ++#include ++#include ++#include ++#include ++ ++#define IPC_STATUS_ADDR 0x04 ++#define IPC_SPTR_ADDR 0x08 ++#define IPC_DPTR_ADDR 0x0C ++#define IPC_READ_BUFFER 0x90 ++#define IPC_WRITE_BUFFER 0x80 ++#define IPC_IOC 0x100 ++ ++/* ++ * Command Register (Write Only): ++ * A write to this register results in an interrupt to the SCU core processor ++ * Format: ++ * |rfu2(8) | size(8) | command id(4) | rfu1(3) | ioc(1) | command(8)| ++ */ ++void intel_scu_ipc_send_command(u32 cmd) ++{ ++ writel(cmd | IPC_IOC, CONFIG_SCU_IPC_BASE); ++} ++ ++/* ++ * IPC Write Buffer (Write Only): ++ * 16-byte buffer for sending data associated with IPC command to ++ * SCU. Size of the data is specified in the IPC_COMMAND_REG register ++ */ ++void ipc_data_writel(u32 data, u32 offset) ++{ /* Write ipc data */ ++ writel(data, CONFIG_SCU_IPC_BASE + IPC_WRITE_BUFFER + offset); ++} ++ ++/* ++ * Status Register (Read Only): ++ * Driver will read this register to get the ready/busy status of the IPC ++ * block and error status of the IPC command that was just processed by SCU ++ * Format: ++ * |rfu3(8)|error code(8)|initiator id(8)|cmd id(4)|rfu1(2)|error(1)|busy(1)| ++ */ ++ ++u32 ipc_read_status(void) ++{ ++ return readl(CONFIG_SCU_IPC_BASE + IPC_STATUS_ADDR); ++} ++ ++u8 ipc_data_readb(u32 offset) ++{ /* Read ipc byte data */ ++ return readb(CONFIG_SCU_IPC_BASE + IPC_READ_BUFFER + offset); ++} ++ ++u32 ipc_data_readl(u32 offset) ++{ /* Read ipc u32 data */ ++ return readl(CONFIG_SCU_IPC_BASE + IPC_READ_BUFFER + offset); ++} ++ ++int intel_scu_ipc_check_status(void) ++{ ++ int ret = 0; ++ int status = 0; ++ int loop_count = 3000000; ++ ++ while ((ipc_read_status() & 1) && --loop_count) ++ udelay(1); ++ if (loop_count == 0) ++ ret = -ETIMEDOUT; ++ ++ status = ipc_read_status(); ++ ++ if (status & 0x2) { ++ printf("%s() status=0x%08x\n", __func__, status); ++ return -EIO; ++ } ++ status++; ++ return ret; ++} ++ ++/** ++ * intel_scu_ipc_simple_command - send a simple command ++ * @cmd: command ++ * @sub: sub type ++ * ++ * Issue a simple command to the SCU. Do not use this interface if ++ * you must then access data as any data values may be overwritten ++ * by another SCU access by the time this function returns. ++ * ++ * This function may sleep. Locking for SCU accesses is handled for ++ * the caller. ++ */ ++int intel_scu_ipc_simple_command(int cmd, int sub) ++{ ++ int err = 0; ++ ++ intel_scu_ipc_send_command(sub << 12 | cmd); ++ err = intel_scu_ipc_check_status(); ++ return err; ++} ++ ++int intel_scu_ipc_raw_cmd(u32 cmd, u32 sub, u8 * in, u8 inlen, u32 * out, ++ u32 outlen, u32 dptr, u32 sptr) ++{ ++ int i, err; ++ u32 wbuf[4]; ++ ++ if (inlen > 16) ++ return -EINVAL; ++ ++ memcpy(wbuf, in, inlen); ++ ++ writel(dptr, CONFIG_SCU_IPC_BASE + IPC_DPTR_ADDR); ++ writel(sptr, CONFIG_SCU_IPC_BASE + IPC_SPTR_ADDR); ++ ++ /** ++ * SRAM controller don't support 8bit write, it only supports ++ * 32bit write, so we have to write into the WBUF in 32bit, ++ * and SCU FW will use the inlen to determine the actual input ++ * data length in the WBUF. ++ */ ++ for (i = 0; i < ((inlen + 3) / 4); i++) ++ ipc_data_writel(wbuf[i], 4 * i); ++ ++ /** ++ * Watchdog IPC command is an exception here using double word ++ * as the unit of input data size because of some historical ++ * reasons and SCU FW is doing so. ++ */ ++ if ((cmd & 0xFF) == IPCMSG_WATCHDOG_TIMER) ++ inlen = (inlen + 3) / 4; ++ ++ intel_scu_ipc_send_command((inlen << 16) | (sub << 12) | cmd); ++ err = intel_scu_ipc_check_status(); ++ ++ for (i = 0; i < outlen; i++) ++ *out++ = ipc_data_readl(4 * i); ++ ++ return err; ++} ++ ++int intel_scu_ipc_command(u32 cmd, u32 sub, u8 * in, u8 inlen, ++ u32 * out, u32 outlen) ++{ ++ int ret; ++ ret = intel_scu_ipc_raw_cmd(cmd, sub, in, inlen, out, outlen, 0, 0); ++ return ret; ++} ++ ++int init_scu_ipc(void) ++{ ++ return 0; ++} +diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile +index 931922b..c88b42b 100644 +--- a/drivers/mmc/Makefile ++++ b/drivers/mmc/Makefile +@@ -30,6 +30,7 @@ obj-$(CONFIG_DWMMC) += dw_mmc.o + obj-$(CONFIG_EXYNOS_DWMMC) += exynos_dw_mmc.o + obj-$(CONFIG_ZYNQ_SDHCI) += zynq_sdhci.o + obj-$(CONFIG_SOCFPGA_DWMMC) += socfpga_dw_mmc.o ++obj-$(CONFIG_TANGIER_SDHCI) += tangier_sdhci.o + ifdef CONFIG_SPL_BUILD + obj-$(CONFIG_SPL_MMC_BOOT) += fsl_esdhc_spl.o + else +diff --git a/drivers/mmc/tangier_sdhci.c b/drivers/mmc/tangier_sdhci.c +new file mode 100644 +index 0000000..3337651 +--- /dev/null ++++ b/drivers/mmc/tangier_sdhci.c +@@ -0,0 +1,38 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++int tangier_sdhci_init(u32 regbase, int index, int bus_width) ++{ ++ struct sdhci_host *host = NULL; ++ ++ host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host)); ++ if (!host) { ++ printf("sdhci__host malloc fail!\n"); ++ return 1; ++ } ++ ++ memset(host, 0x00, sizeof(struct sdhci_host)); ++ ++ host->name = "tangier_sdhci"; ++ host->ioaddr = (void *)regbase; ++ ++ host->quirks = SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE | ++ SDHCI_QUIRK_BROKEN_R1B | SDHCI_QUIRK_32BIT_DMA_ADDR | ++ SDHCI_QUIRK_WAIT_SEND_CMD; ++ host->voltages = MMC_VDD_165_195; //MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; ++ host->version = sdhci_readw(host, SDHCI_HOST_VERSION); ++ ++ host->index = index; ++ ++ host->host_caps = MMC_MODE_HC; ++ ++ add_sdhci(host, 200000000, 400000); ++ ++ return 0; ++} +diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile +index 571c18f..1bdf0f5 100644 +--- a/drivers/serial/Makefile ++++ b/drivers/serial/Makefile +@@ -6,6 +6,7 @@ + # + + obj-y += serial.o ++obj-y += serial_tng.o + + obj-$(CONFIG_ALTERA_UART) += altera_uart.o + obj-$(CONFIG_ALTERA_JTAG_UART) += altera_jtag_uart.o +diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c +index df05bde..9e493dc 100644 +--- a/drivers/serial/serial.c ++++ b/drivers/serial/serial.c +@@ -159,6 +159,7 @@ serial_initfunc(sa1100_serial_initialize); + serial_initfunc(sh_serial_initialize); + serial_initfunc(arm_dcc_initialize); + serial_initfunc(mxs_auart_initialize); ++serial_initfunc(tng_serial_initialize); + serial_initfunc(arc_serial_initialize); + + /** +diff --git a/drivers/serial/serial_tng.c b/drivers/serial/serial_tng.c +new file mode 100644 +index 0000000..d6d6753 +--- /dev/null ++++ b/drivers/serial/serial_tng.c +@@ -0,0 +1,252 @@ ++/* ++ * (C) Copyright 2002 ++ * Gary Jennejohn, DENX Software Engineering, ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++ ++#include ++#include ++ ++#include ++ ++#define cpu_relax() asm volatile("rep; nop") ++ ++#define UART_GLOBAL 0xff010000 ++ ++#define UART_OFFSET 0x80 ++ ++#define SERIAL_BASE_ADDR (UART_GLOBAL + (UART_OFFSET * (dev_index + 1))) ++ ++#define XMTRDY 0x20 ++#define LSR_DR (0x01) ++ ++#define DLAB 0x80 ++ ++#define TXR 0 /* Transmit register (WRITE) */ ++#define RXR 0 /* Receive register (READ) */ ++#define IER 1 /* Interrupt Enable */ ++#define IIR 2 /* Interrupt ID */ ++#define FCR 2 /* FIFO control */ ++#define LCR 3 /* Line control */ ++#define MCR 4 /* Modem control */ ++#define LSR 5 /* Line Status */ ++#define MSR 6 /* Modem Status */ ++#define DLL 0 /* Divisor Latch Low */ ++#define DLH 1 /* Divisor latch High */ ++#define MUL 0x34 /* DDS Multiplier */ ++#define DIV 0x38 /* DDS Divisor */ ++ ++/* Multi serial device functions */ ++#define DECLARE_TNG_SERIAL_FUNCTIONS(port) \ ++ int tng_serial##port##_init(void) \ ++ { \ ++ return serial_init_dev(port); \ ++ } \ ++ int tng_serial##port##_shutdown(void) \ ++ { \ ++ return serial_shutdown_dev(port); \ ++ } \ ++ void tng_serial##port##_setbrg(void) \ ++ { \ ++ serial_setbrg_dev(port); \ ++ } \ ++ int tng_serial##port##_getc(void) \ ++ { \ ++ return serial_getc_dev(port); \ ++ } \ ++ int tng_serial##port##_tstc(void) \ ++ { \ ++ return serial_tstc_dev(port); \ ++ } \ ++ void tng_serial##port##_putc(const char c) \ ++ { \ ++ serial_putc_dev(port, c); \ ++ } \ ++ void tng_serial##port##_puts(const char *s) \ ++ { \ ++ serial_puts_dev(port, s); \ ++ } ++ ++#define INIT_TNG_SERIAL_STRUCTURE(port, __name) { \ ++ .name = __name, \ ++ .start = tng_serial##port##_init, \ ++ .stop = tng_serial##port##_shutdown, \ ++ .setbrg = tng_serial##port##_setbrg, \ ++ .getc = tng_serial##port##_getc, \ ++ .tstc = tng_serial##port##_tstc, \ ++ .putc = tng_serial##port##_putc, \ ++ .puts = tng_serial##port##_puts, \ ++} ++ ++#ifdef CONFIG_HWFLOW ++static int hwflow; ++#endif ++ ++void _serial_setbrg(const int dev_index) ++{ ++ return; ++} ++ ++static inline void serial_setbrg_dev(unsigned int dev_index) ++{ ++ _serial_setbrg(dev_index); ++} ++ ++/* ++ * Initialise the serial port. ++ */ ++static int serial_init_dev(const int dev_index) ++{ ++ unsigned char c; ++ unsigned divisor = 16; ++ ++ c = readb(SERIAL_BASE_ADDR + LCR); ++ writeb(c | DLAB, SERIAL_BASE_ADDR + LCR); ++ writeb(divisor & 0xff, SERIAL_BASE_ADDR + DLL); ++ writeb((divisor >> 8) & 0xff, SERIAL_BASE_ADDR + DLH); ++ writeb(c & ~DLAB, SERIAL_BASE_ADDR + LCR); ++ writeb(0x2, SERIAL_BASE_ADDR + IER); /* TIE enable */ ++ writeb(0x3, SERIAL_BASE_ADDR + LCR); /* 8n1 */ ++ writeb(0, SERIAL_BASE_ADDR + FCR); /* no fifo */ ++ writeb(0x3, SERIAL_BASE_ADDR + MCR); /* DTR + RTS */ ++ writeb(7, SERIAL_BASE_ADDR + FCR); ++ writel(0x2ee0, SERIAL_BASE_ADDR + MUL); ++ writel(0x3d09, SERIAL_BASE_ADDR + DIV); ++ ++ return 0; ++} ++ ++/* ++ * Shutdown the serial port. ++ */ ++static int serial_shutdown_dev(const int dev_index) ++{ ++ unsigned char c; ++ ++ writeb(0x0, SERIAL_BASE_ADDR + IER); /* TIE disable */ ++ c = readb(SERIAL_BASE_ADDR + MCR); ++ writeb(c & ~0x08, SERIAL_BASE_ADDR + MCR); /* DTR + RTS */ ++ c = readb(SERIAL_BASE_ADDR + LCR); ++ writeb(c & ~0x40, SERIAL_BASE_ADDR + LCR); ++ writeb(0x7, SERIAL_BASE_ADDR + FCR); ++ writeb(0x0, SERIAL_BASE_ADDR + FCR); /* no fifo */ ++ ++ return 0; ++} ++ ++/* ++ * Read a single byte from the serial port. ++ */ ++int _serial_getc(const int dev_index) ++{ ++ while ((readb(SERIAL_BASE_ADDR + LSR) & LSR_DR) == 0) { ++ if (gd->have_console) ++ break; ++ } ++ ++ return readb(SERIAL_BASE_ADDR + RXR) & 0xff; ++} ++ ++static inline int serial_getc_dev(unsigned int dev_index) ++{ ++ return _serial_getc(dev_index); ++} ++ ++/* ++ * Output a single byte to the serial port. ++ */ ++void _serial_putc(const char c, const int dev_index) ++{ ++ while ((readb(SERIAL_BASE_ADDR + LSR) & XMTRDY) == 0) ++ cpu_relax(); ++ ++ writeb(c, SERIAL_BASE_ADDR + TXR); ++ ++ /* If \n, also do \r */ ++ if (c == '\n') ++ serial_putc('\r'); ++} ++ ++static inline void serial_putc_dev(unsigned int dev_index, const char c) ++{ ++ _serial_putc(c, dev_index); ++} ++ ++/* ++ * Test whether a character is in the RX buffer ++ */ ++int _serial_tstc(const int dev_index) ++{ ++ return (readb(SERIAL_BASE_ADDR + LSR) & LSR_DR) != 0; ++} ++ ++static inline int serial_tstc_dev(unsigned int dev_index) ++{ ++ return _serial_tstc(dev_index); ++} ++ ++void _serial_puts(const char *s, const int dev_index) ++{ ++ while (*s) { ++ _serial_putc(*s++, dev_index); ++ } ++ return; ++} ++ ++static inline void serial_puts_dev(int dev_index, const char *s) ++{ ++ return _serial_puts(s, dev_index); ++} ++ ++DECLARE_TNG_SERIAL_FUNCTIONS(0); ++struct serial_device tng_serial0_device = ++INIT_TNG_SERIAL_STRUCTURE(0, "tng_serial0"); ++DECLARE_TNG_SERIAL_FUNCTIONS(1); ++struct serial_device tng_serial1_device = ++INIT_TNG_SERIAL_STRUCTURE(1, "tng_serial1"); ++DECLARE_TNG_SERIAL_FUNCTIONS(2); ++struct serial_device tng_serial2_device = ++INIT_TNG_SERIAL_STRUCTURE(2, "tng_serial2"); ++ ++__weak struct serial_device *default_serial_console(void) ++{ ++#if defined(CONFIG_SYS_TNG_SERIAL0) ++ return &tng_serial0_device; ++#elif defined(CONFIG_SYS_TNG_SERIAL1) ++ return &tng_serial1_device; ++#elif defined(CONFIG_SYS_TNG_SERIAL2) ++ return &tng_serial2_device; ++#else ++#error "CONFIG_SERIAL? missing." ++#endif ++} ++ ++void tng_serial_initialize(void) ++{ ++#if defined(CONFIG_SYS_TNG_SERIAL0) ++ serial_init_dev(0); ++ serial_register(&tng_serial0_device); ++#endif ++#if defined(CONFIG_SYS_TNG_SERIAL1) ++ serial_init_dev(1); ++ serial_register(&tng_serial1_device); ++#endif ++#if defined(CONFIG_SYS_TNG_SERIAL2) ++ serial_init_dev(2); ++ serial_register(&tng_serial2_device); ++#endif ++} +diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig +new file mode 100644 +index 0000000..2c05ec9 +--- /dev/null ++++ b/drivers/usb/dwc3/Kconfig +@@ -0,0 +1,26 @@ ++config USB_DWC3 ++ tristate "DesignWare USB3 DRD Core Support" ++ depends on (USB || USB_GADGET) ++ select USB_OTG_UTILS ++ select USB_XHCI_PLATFORM ++ help ++ Say Y or M here if your system has a Dual Role SuperSpeed ++ USB controller based on the DesignWare USB3 IP Core. ++ ++ If you choose to build this driver is a dynamically linked ++ module, the module will be called dwc3.ko. ++ ++if USB_DWC3 ++ ++config USB_DWC3_DEBUG ++ bool "Enable Debugging Messages" ++ help ++ Say Y here to enable debugging messages on DWC3 Driver. ++ ++config USB_DWC3_VERBOSE ++ bool "Enable Verbose Debugging Messages" ++ depends on USB_DWC3_DEBUG ++ help ++ Say Y here to enable verbose debugging messages on DWC3 Driver. ++ ++endif +diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile +new file mode 100644 +index 0000000..1b5d353 +--- /dev/null ++++ b/drivers/usb/dwc3/Makefile +@@ -0,0 +1,8 @@ ++obj-$(CONFIG_USB_DWC3) += dwc3_core.o dwc3_ep0.o dwc3_misc.o ++obj-$(CONFIG_USB_DWC3_GADGET) += dwc3_gadget.o ++obj-$(CONFIG_USB_DWC3_HOST) += dwc3_host.o ++ ++ccflags-y := $(call cc-option,-Wno-unused-variable) \ ++ $(call cc-option,-Wno-unused-but-set-variable) \ ++ $(call cc-option,-Wno-unused-label) ++ +diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h +new file mode 100644 +index 0000000..46f0c2f +--- /dev/null ++++ b/drivers/usb/dwc3/core.h +@@ -0,0 +1,814 @@ ++/** ++ * core.h - DesignWare USB3 DRD Core Header ++ * ++ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com ++ * ++ * Authors: Felipe Balbi , ++ * Sebastian Andrzej Siewior ++ * ++ * 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 the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not 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. ++ * ++ * 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 MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. ++ */ ++ ++#ifndef __DRIVERS_USB_DWC3_CORE_H ++#define __DRIVERS_USB_DWC3_CORE_H ++ ++#include ++#include ++#include ++#include "misc.h" ++ ++/* Global constants */ ++#define DWC3_ENDPOINTS_NUM 32 ++ ++#define DWC3_EVENT_BUFFERS_SIZE PAGE_SIZE ++#define DWC3_EVENT_TYPE_MASK 0xfe ++ ++#define DWC3_EVENT_TYPE_DEV 0 ++#define DWC3_EVENT_TYPE_CARKIT 3 ++#define DWC3_EVENT_TYPE_I2C 4 ++ ++#define DWC3_DEVICE_EVENT_DISCONNECT 0 ++#define DWC3_DEVICE_EVENT_RESET 1 ++#define DWC3_DEVICE_EVENT_CONNECT_DONE 2 ++#define DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE 3 ++#define DWC3_DEVICE_EVENT_WAKEUP 4 ++#define DWC3_DEVICE_EVENT_EOPF 6 ++#define DWC3_DEVICE_EVENT_SOF 7 ++#define DWC3_DEVICE_EVENT_ERRATIC_ERROR 9 ++#define DWC3_DEVICE_EVENT_CMD_CMPL 10 ++#define DWC3_DEVICE_EVENT_OVERFLOW 11 ++ ++#define DWC3_GEVNTCOUNT_MASK 0xfffc ++#define DWC3_GSNPSID_MASK 0xffff0000 ++#define DWC3_GSNPSREV_MASK 0xffff ++ ++/* Global Registers */ ++#define DWC3_GSBUSCFG0 0xc100 ++#define DWC3_GSBUSCFG1 0xc104 ++#define DWC3_GTXTHRCFG 0xc108 ++#define DWC3_GRXTHRCFG 0xc10c ++#define DWC3_GCTL 0xc110 ++#define DWC3_GEVTEN 0xc114 ++#define DWC3_GSTS 0xc118 ++#define DWC3_GSNPSID 0xc120 ++#define DWC3_GGPIO 0xc124 ++#define DWC3_GUID 0xc128 ++#define DWC3_GUCTL 0xc12c ++#define DWC3_GBUSERRADDR0 0xc130 ++#define DWC3_GBUSERRADDR1 0xc134 ++#define DWC3_GPRTBIMAP0 0xc138 ++#define DWC3_GPRTBIMAP1 0xc13c ++#define DWC3_GHWPARAMS0 0xc140 ++#define DWC3_GHWPARAMS1 0xc144 ++#define DWC3_GHWPARAMS2 0xc148 ++#define DWC3_GHWPARAMS3 0xc14c ++#define DWC3_GHWPARAMS4 0xc150 ++#define DWC3_GHWPARAMS5 0xc154 ++#define DWC3_GHWPARAMS6 0xc158 ++#define DWC3_GHWPARAMS7 0xc15c ++#define DWC3_GDBGFIFOSPACE 0xc160 ++#define DWC3_GDBGLTSSM 0xc164 ++#define DWC3_GPRTBIMAP_HS0 0xc180 ++#define DWC3_GPRTBIMAP_HS1 0xc184 ++#define DWC3_GPRTBIMAP_FS0 0xc188 ++#define DWC3_GPRTBIMAP_FS1 0xc18c ++ ++#define DWC3_GUSB2PHYCFG(n) (0xc200 + (n * 0x04)) ++#define DWC3_GUSB2I2CCTL(n) (0xc240 + (n * 0x04)) ++ ++#define DWC3_GUSB2PHYACC(n) (0xc280 + (n * 0x04)) ++ ++#define DWC3_GUSB3PIPECTL(n) (0xc2c0 + (n * 0x04)) ++ ++#define DWC3_GTXFIFOSIZ(n) (0xc300 + (n * 0x04)) ++#define DWC3_GRXFIFOSIZ(n) (0xc380 + (n * 0x04)) ++ ++#define DWC3_GEVNTADRLO(n) (0xc400 + (n * 0x10)) ++#define DWC3_GEVNTADRHI(n) (0xc404 + (n * 0x10)) ++#define DWC3_GEVNTSIZ(n) (0xc408 + (n * 0x10)) ++#define DWC3_GEVNTCOUNT(n) (0xc40c + (n * 0x10)) ++ ++#define DWC3_GHWPARAMS8 0xc600 ++ ++/* Device Registers */ ++#define DWC3_DCFG 0xc700 ++#define DWC3_DCTL 0xc704 ++#define DWC3_DEVTEN 0xc708 ++#define DWC3_DSTS 0xc70c ++#define DWC3_DGCMDPAR 0xc710 ++#define DWC3_DGCMD 0xc714 ++#define DWC3_DALEPENA 0xc720 ++#define DWC3_DEPCMDPAR2(n) (0xc800 + (n * 0x10)) ++#define DWC3_DEPCMDPAR1(n) (0xc804 + (n * 0x10)) ++#define DWC3_DEPCMDPAR0(n) (0xc808 + (n * 0x10)) ++#define DWC3_DEPCMD(n) (0xc80c + (n * 0x10)) ++ ++/* OTG Registers */ ++#define DWC3_OCFG 0xcc00 ++#define DWC3_OCTL 0xcc04 ++#define DWC3_OEVTEN 0xcc08 ++#define DWC3_OSTS 0xcc0C ++ ++/* Bit fields */ ++ ++/* Global Configuration Register */ ++#define DWC3_GCTL_PWRDNSCALE(n) (n << 19) ++#define DWC3_GCTL_U2RSTECN (1 << 16) ++#define DWC3_GCTL_RAMCLKSEL(x) ((x & DWC3_GCTL_CLK_MASK) << 6) ++#define DWC3_GCTL_CLK_BUS (0) ++#define DWC3_GCTL_CLK_PIPE (1) ++#define DWC3_GCTL_CLK_PIPEHALF (2) ++#define DWC3_GCTL_CLK_MASK (3) ++ ++#define DWC3_GCTL_PRTCAP(n) (((n) & (3 << 12)) >> 12) ++#define DWC3_GCTL_PRTCAPDIR(n) (n << 12) ++#define DWC3_GCTL_PRTCAP_HOST 1 ++#define DWC3_GCTL_PRTCAP_DEVICE 2 ++#define DWC3_GCTL_PRTCAP_OTG 3 ++ ++#define DWC3_GCTL_CORESOFTRESET (1 << 11) ++#define DWC3_GCTL_SCALEDOWN(n) (n << 4) ++#define DWC3_GCTL_DISSCRAMBLE (1 << 3) ++#define DWC3_GCTL_DSBLCLKGTNG (1 << 0) ++ ++/* Global USB2 PHY Configuration Register */ ++#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31) ++#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6) ++ ++/* Global USB3 PIPE Control Register */ ++#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31) ++#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17) ++ ++/* Global HWPARAMS1 Register */ ++#define DWC3_GHWPARAMS1_EN_PWROPT(n) ((n & (3 << 24)) >> 24) ++#define DWC3_GHWPARAMS1_EN_PWROPT_NO 0 ++#define DWC3_GHWPARAMS1_EN_PWROPT_CLK 1 ++ ++/* Device Configuration Register */ ++#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3) ++#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f) ++ ++#define DWC3_DCFG_SPEED_MASK (7 << 0) ++#define DWC3_DCFG_SUPERSPEED (4 << 0) ++#define DWC3_DCFG_HIGHSPEED (0 << 0) ++#define DWC3_DCFG_FULLSPEED2 (1 << 0) ++#define DWC3_DCFG_LOWSPEED (2 << 0) ++#define DWC3_DCFG_FULLSPEED1 (3 << 0) ++ ++/* Device Control Register */ ++#define DWC3_DCTL_RUN_STOP (1 << 31) ++#define DWC3_DCTL_CSFTRST (1 << 30) ++#define DWC3_DCTL_LSFTRST (1 << 29) ++ ++#define DWC3_DCTL_HIRD_THRES_MASK (0x1f << 24) ++#define DWC3_DCTL_HIRD_THRES(n) (((n) & DWC3_DCTL_HIRD_THRES_MASK) >> 24) ++ ++#define DWC3_DCTL_APPL1RES (1 << 23) ++ ++#define DWC3_DCTL_INITU2ENA (1 << 12) ++#define DWC3_DCTL_ACCEPTU2ENA (1 << 11) ++#define DWC3_DCTL_INITU1ENA (1 << 10) ++#define DWC3_DCTL_ACCEPTU1ENA (1 << 9) ++#define DWC3_DCTL_TSTCTRL_MASK (0xf << 1) ++ ++#define DWC3_DCTL_ULSTCHNGREQ_MASK (0x0f << 5) ++#define DWC3_DCTL_ULSTCHNGREQ(n) (((n) << 5) & DWC3_DCTL_ULSTCHNGREQ_MASK) ++ ++#define DWC3_DCTL_ULSTCHNG_NO_ACTION (DWC3_DCTL_ULSTCHNGREQ(0)) ++#define DWC3_DCTL_ULSTCHNG_SS_DISABLED (DWC3_DCTL_ULSTCHNGREQ(4)) ++#define DWC3_DCTL_ULSTCHNG_RX_DETECT (DWC3_DCTL_ULSTCHNGREQ(5)) ++#define DWC3_DCTL_ULSTCHNG_SS_INACTIVE (DWC3_DCTL_ULSTCHNGREQ(6)) ++#define DWC3_DCTL_ULSTCHNG_RECOVERY (DWC3_DCTL_ULSTCHNGREQ(8)) ++#define DWC3_DCTL_ULSTCHNG_COMPLIANCE (DWC3_DCTL_ULSTCHNGREQ(10)) ++#define DWC3_DCTL_ULSTCHNG_LOOPBACK (DWC3_DCTL_ULSTCHNGREQ(11)) ++ ++/* Device Event Enable Register */ ++#define DWC3_DEVTEN_VNDRDEVTSTRCVEDEN (1 << 12) ++#define DWC3_DEVTEN_EVNTOVERFLOWEN (1 << 11) ++#define DWC3_DEVTEN_CMDCMPLTEN (1 << 10) ++#define DWC3_DEVTEN_ERRTICERREN (1 << 9) ++#define DWC3_DEVTEN_SOFEN (1 << 7) ++#define DWC3_DEVTEN_EOPFEN (1 << 6) ++#define DWC3_DEVTEN_WKUPEVTEN (1 << 4) ++#define DWC3_DEVTEN_ULSTCNGEN (1 << 3) ++#define DWC3_DEVTEN_CONNECTDONEEN (1 << 2) ++#define DWC3_DEVTEN_USBRSTEN (1 << 1) ++#define DWC3_DEVTEN_DISCONNEVTEN (1 << 0) ++ ++/* Device Status Register */ ++#define DWC3_DSTS_PWRUPREQ (1 << 24) ++#define DWC3_DSTS_COREIDLE (1 << 23) ++#define DWC3_DSTS_DEVCTRLHLT (1 << 22) ++ ++#define DWC3_DSTS_USBLNKST_MASK (0x0f << 18) ++#define DWC3_DSTS_USBLNKST(n) (((n) & DWC3_DSTS_USBLNKST_MASK) >> 18) ++ ++#define DWC3_DSTS_RXFIFOEMPTY (1 << 17) ++ ++#define DWC3_DSTS_SOFFN_MASK (0x3ff << 3) ++#define DWC3_DSTS_SOFFN(n) (((n) & DWC3_DSTS_SOFFN_MASK) >> 3) ++ ++#define DWC3_DSTS_CONNECTSPD (7 << 0) ++ ++#define DWC3_DSTS_SUPERSPEED (4 << 0) ++#define DWC3_DSTS_HIGHSPEED (0 << 0) ++#define DWC3_DSTS_FULLSPEED2 (1 << 0) ++#define DWC3_DSTS_LOWSPEED (2 << 0) ++#define DWC3_DSTS_FULLSPEED1 (3 << 0) ++ ++/* Device Generic Command Register */ ++#define DWC3_DGCMD_SET_LMP 0x01 ++#define DWC3_DGCMD_SET_PERIODIC_PAR 0x02 ++#define DWC3_DGCMD_XMIT_FUNCTION 0x03 ++#define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09 ++#define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a ++#define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c ++#define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10 ++ ++/* Device Endpoint Command Register */ ++#define DWC3_DEPCMD_PARAM_SHIFT 16 ++#define DWC3_DEPCMD_PARAM(x) (x << DWC3_DEPCMD_PARAM_SHIFT) ++#define DWC3_DEPCMD_GET_RSC_IDX(x) ((x >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f) ++#define DWC3_DEPCMD_STATUS_MASK (0x0f << 12) ++#define DWC3_DEPCMD_STATUS(x) ((x & DWC3_DEPCMD_STATUS_MASK) >> 12) ++#define DWC3_DEPCMD_HIPRI_FORCERM (1 << 11) ++#define DWC3_DEPCMD_CMDACT (1 << 10) ++#define DWC3_DEPCMD_CMDIOC (1 << 8) ++ ++#define DWC3_DEPCMD_DEPSTARTCFG (0x09 << 0) ++#define DWC3_DEPCMD_ENDTRANSFER (0x08 << 0) ++#define DWC3_DEPCMD_UPDATETRANSFER (0x07 << 0) ++#define DWC3_DEPCMD_STARTTRANSFER (0x06 << 0) ++#define DWC3_DEPCMD_CLEARSTALL (0x05 << 0) ++#define DWC3_DEPCMD_SETSTALL (0x04 << 0) ++#define DWC3_DEPCMD_GETSEQNUMBER (0x03 << 0) ++#define DWC3_DEPCMD_SETTRANSFRESOURCE (0x02 << 0) ++#define DWC3_DEPCMD_SETEPCONFIG (0x01 << 0) ++ ++/* The EP number goes 0..31 so ep0 is always out and ep1 is always in */ ++#define DWC3_DALEPENA_EP(n) (1 << n) ++ ++#define DWC3_DEPCMD_TYPE_CONTROL 0 ++#define DWC3_DEPCMD_TYPE_ISOC 1 ++#define DWC3_DEPCMD_TYPE_BULK 2 ++#define DWC3_DEPCMD_TYPE_INTR 3 ++ ++/* Structures */ ++ ++struct dwc3_trb_hw; ++ ++/** ++ * struct dwc3_event_buffer - Software event buffer representation ++ * @list: a list of event buffers ++ * @buf: _THE_ buffer ++ * @length: size of this buffer ++ * @dma: dma_addr_t ++ * @dwc: pointer to DWC controller ++ */ ++struct dwc3_event_buffer { ++ void *buf; ++ unsigned length; ++ unsigned int lpos; ++ ++ dma_addr_t dma; ++ ++ struct dwc3 *dwc; ++}; ++ ++#define DWC3_EP_FLAG_STALLED (1 << 0) ++#define DWC3_EP_FLAG_WEDGED (1 << 1) ++ ++#define DWC3_EP_DIRECTION_TX true ++#define DWC3_EP_DIRECTION_RX false ++ ++#define DWC3_TRB_NUM 32 ++#define DWC3_TRB_MASK (DWC3_TRB_NUM - 1) ++ ++/** ++ * struct dwc3_ep - device side endpoint representation ++ * @endpoint: usb endpoint ++ * @request_list: list of requests for this endpoint ++ * @req_queued: list of requests on this ep which have TRBs setup ++ * @trb_pool: array of transaction buffers ++ * @trb_pool_dma: dma address of @trb_pool ++ * @free_slot: next slot which is going to be used ++ * @busy_slot: first slot which is owned by HW ++ * @desc: usb_endpoint_descriptor pointer ++ * @dwc: pointer to DWC controller ++ * @flags: endpoint flags (wedged, stalled, ...) ++ * @current_trb: index of current used trb ++ * @number: endpoint number (1 - 15) ++ * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK ++ * @res_trans_idx: Resource transfer index ++ * @interval: the intervall on which the ISOC transfer is started ++ * @name: a human readable name e.g. ep1out-bulk ++ * @direction: true for TX, false for RX ++ * @stream_capable: true when streams are enabled ++ */ ++struct dwc3_ep { ++ struct usb_ep endpoint; ++ struct list_head request_list; ++ struct list_head req_queued; ++ ++ struct dwc3_trb_hw *trb_pool; ++ dma_addr_t trb_pool_dma; ++ u32 free_slot; ++ u32 busy_slot; ++ const struct usb_endpoint_descriptor *desc; ++ const struct usb_ss_ep_comp_descriptor *comp_desc; ++ struct dwc3 *dwc; ++ ++ unsigned flags; ++#define DWC3_EP_ENABLED (1 << 0) ++#define DWC3_EP_STALL (1 << 1) ++#define DWC3_EP_WEDGE (1 << 2) ++#define DWC3_EP_BUSY (1 << 4) ++#define DWC3_EP_PENDING_REQUEST (1 << 5) ++ ++ /* This last one is specific to EP0 */ ++#define DWC3_EP0_DIR_IN (1 << 31) ++ ++ unsigned current_trb; ++ ++ u8 number; ++ u8 type; ++ u8 res_trans_idx; ++ u32 interval; ++ ++ char name[20]; ++ ++ unsigned direction:1; ++ unsigned stream_capable:1; ++}; ++ ++enum dwc3_phy { ++ DWC3_PHY_UNKNOWN = 0, ++ DWC3_PHY_USB3, ++ DWC3_PHY_USB2, ++}; ++ ++enum dwc3_ep0_next { ++ DWC3_EP0_UNKNOWN = 0, ++ DWC3_EP0_COMPLETE, ++ DWC3_EP0_NRDY_SETUP, ++ DWC3_EP0_NRDY_DATA, ++ DWC3_EP0_NRDY_STATUS, ++}; ++ ++enum dwc3_ep0_state { ++ EP0_UNCONNECTED = 0, ++ EP0_SETUP_PHASE, ++ EP0_DATA_PHASE, ++ EP0_STATUS_PHASE, ++}; ++ ++enum dwc3_link_state { ++ /* In SuperSpeed */ ++ DWC3_LINK_STATE_U0 = 0x00, /* in HS, means ON */ ++ DWC3_LINK_STATE_U1 = 0x01, ++ DWC3_LINK_STATE_U2 = 0x02, /* in HS, means SLEEP */ ++ DWC3_LINK_STATE_U3 = 0x03, /* in HS, means SUSPEND */ ++ DWC3_LINK_STATE_SS_DIS = 0x04, ++ DWC3_LINK_STATE_RX_DET = 0x05, /* in HS, means Early Suspend */ ++ DWC3_LINK_STATE_SS_INACT = 0x06, ++ DWC3_LINK_STATE_POLL = 0x07, ++ DWC3_LINK_STATE_RECOV = 0x08, ++ DWC3_LINK_STATE_HRESET = 0x09, ++ DWC3_LINK_STATE_CMPLY = 0x0a, ++ DWC3_LINK_STATE_LPBK = 0x0b, ++ DWC3_LINK_STATE_MASK = 0x0f, ++}; ++ ++enum dwc3_device_state { ++ DWC3_DEFAULT_STATE, ++ DWC3_ADDRESS_STATE, ++ DWC3_CONFIGURED_STATE, ++}; ++ ++/** ++ * struct dwc3_trb - transfer request block ++ * @bpl: lower 32bit of the buffer ++ * @bph: higher 32bit of the buffer ++ * @length: buffer size (up to 16mb - 1) ++ * @pcm1: packet count m1 ++ * @trbsts: trb status ++ * 0 = ok ++ * 1 = missed isoc ++ * 2 = setup pending ++ * @hwo: hardware owner of descriptor ++ * @lst: last trb ++ * @chn: chain buffers ++ * @csp: continue on short packets (only supported on isoc eps) ++ * @trbctl: trb control ++ * 1 = normal ++ * 2 = control-setup ++ * 3 = control-status-2 ++ * 4 = control-status-3 ++ * 5 = control-data (first trb of data stage) ++ * 6 = isochronous-first (first trb of service interval) ++ * 7 = isochronous ++ * 8 = link trb ++ * others = reserved ++ * @isp_imi: interrupt on short packet / interrupt on missed isoc ++ * @ioc: interrupt on complete ++ * @sid_sofn: Stream ID / SOF Number ++ */ ++struct dwc3_trb { ++ u64 bplh; ++ ++ union { ++ struct { ++ u32 length:24; ++ u32 pcm1:2; ++ u32 reserved27_26:2; ++ u32 trbsts:4; ++#define DWC3_TRB_STS_OKAY 0 ++#define DWC3_TRB_STS_MISSED_ISOC 1 ++#define DWC3_TRB_STS_SETUP_PENDING 2 ++ }; ++ u32 len_pcm; ++ }; ++ ++ union { ++ struct { ++ u32 hwo:1; ++ u32 lst:1; ++ u32 chn:1; ++ u32 csp:1; ++ u32 trbctl:6; ++ u32 isp_imi:1; ++ u32 ioc:1; ++ u32 reserved13_12:2; ++ u32 sid_sofn:16; ++ u32 reserved31_30:2; ++ }; ++ u32 control; ++ }; ++} __packed; ++ ++/** ++ * struct dwc3_trb_hw - transfer request block (hw format) ++ * @bpl: DW0-3 ++ * @bph: DW4-7 ++ * @size: DW8-B ++ * @trl: DWC-F ++ */ ++struct dwc3_trb_hw { ++ __le32 bpl; ++ __le32 bph; ++ __le32 size; ++ __le32 ctrl; ++} __packed; ++ ++static inline void dwc3_trb_to_hw(struct dwc3_trb *nat, struct dwc3_trb_hw *hw) ++{ ++ hw->bpl = cpu_to_le32(lower_32_bits(nat->bplh)); ++ hw->bph = cpu_to_le32(upper_32_bits(nat->bplh)); ++ hw->size = cpu_to_le32p(&nat->len_pcm); ++ /* HWO is written last */ ++ hw->ctrl = cpu_to_le32p(&nat->control); ++} ++ ++static inline void dwc3_trb_to_nat(struct dwc3_trb_hw *hw, struct dwc3_trb *nat) ++{ ++ u64 bplh; ++ ++ bplh = le32_to_cpup(&hw->bpl); ++ bplh |= (u64) le32_to_cpup(&hw->bph) << 32; ++ nat->bplh = bplh; ++ ++ nat->len_pcm = le32_to_cpup(&hw->size); ++ nat->control = le32_to_cpup(&hw->ctrl); ++} ++ ++/** ++ * dwc3_hwparams - copy of HWPARAMS registers ++ * @hwparams0 - GHWPARAMS0 ++ * @hwparams1 - GHWPARAMS1 ++ * @hwparams2 - GHWPARAMS2 ++ * @hwparams3 - GHWPARAMS3 ++ * @hwparams4 - GHWPARAMS4 ++ * @hwparams5 - GHWPARAMS5 ++ * @hwparams6 - GHWPARAMS6 ++ * @hwparams7 - GHWPARAMS7 ++ * @hwparams8 - GHWPARAMS8 ++ */ ++struct dwc3_hwparams { ++ u32 hwparams0; ++ u32 hwparams1; ++ u32 hwparams2; ++ u32 hwparams3; ++ u32 hwparams4; ++ u32 hwparams5; ++ u32 hwparams6; ++ u32 hwparams7; ++ u32 hwparams8; ++}; ++ ++/* HWPARAMS0 */ ++#define DWC3_MODE(n) ((n) & 0x7) ++ ++#define DWC3_MODE_DEVICE 0 ++#define DWC3_MODE_HOST 1 ++#define DWC3_MODE_DRD 2 ++#define DWC3_MODE_HUB 3 ++ ++/* HWPARAMS1 */ ++#define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15) ++ ++struct dwc3_request { ++ struct usb_request request; ++ struct list_head list; ++ struct dwc3_ep *dep; ++ ++ u8 epnum; ++ struct dwc3_trb_hw *trb; ++ dma_addr_t trb_dma; ++ ++ unsigned direction:1; ++ unsigned mapped:1; ++ unsigned queued:1; ++}; ++ ++/** ++ * struct dwc3 - representation of our controller ++ * @ctrl_req: usb control request which is used for ep0 ++ * @ep0_trb: trb which is used for the ctrl_req ++ * @ep0_bounce: bounce buffer for ep0 ++ * @setup_buf: used while precessing STD USB requests ++ * @ctrl_req_addr: dma address of ctrl_req ++ * @ep0_trb: dma address of ep0_trb ++ * @ep0_usb_req: dummy req used while handling STD USB requests ++ * @setup_buf_addr: dma address of setup_buf ++ * @ep0_bounce_addr: dma address of ep0_bounce ++ * @lock: for synchronizing ++ * @dev: pointer to our struct device ++ * @xhci: pointer to our xHCI child ++ * @event_buffer_list: a list of event buffers ++ * @gadget: device side representation of the peripheral controller ++ * @gadget_driver: pointer to the gadget driver ++ * @regs: base address for our registers ++ * @regs_size: address space size ++ * @irq: IRQ number ++ * @num_event_buffers: calculated number of event buffers ++ * @u1u2: only used on revisions <1.83a for workaround ++ * @maximum_speed: maximum speed requested (mainly for testing purposes) ++ * @revision: revision register contents ++ * @mode: mode of operation ++ * @is_selfpowered: true when we are selfpowered ++ * @three_stage_setup: set if we perform a three phase setup ++ * @ep0_bounced: true when we used bounce buffer ++ * @ep0_expect_in: true when we expect a DATA IN transfer ++ * @start_config_issued: true when StartConfig command has been issued ++ * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround ++ * @ep0_next_event: hold the next expected event ++ * @ep0state: state of endpoint zero ++ * @link_state: link state ++ * @speed: device speed (super, high, full, low) ++ * @mem: points to start of memory which is used for this struct. ++ * @hwparams: copy of hwparams registers ++ * @root: debugfs root folder pointer ++ */ ++struct dwc3 { ++ struct usb_ctrlrequest *ctrl_req; ++ struct dwc3_trb_hw *ep0_trb; ++ void *ep0_bounce; ++ u8 *setup_buf; ++ dma_addr_t ctrl_req_addr; ++ dma_addr_t ep0_trb_addr; ++ dma_addr_t setup_buf_addr; ++ dma_addr_t ep0_bounce_addr; ++ struct dwc3_request ep0_usb_req; ++ /* device lock */ ++ spinlock_t lock; ++ struct device *dev; ++ ++ struct platform_device *xhci; ++ struct resource *res; ++ ++ struct dwc3_event_buffer **ev_buffs; ++ struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM]; ++ ++ struct usb_gadget gadget; ++ struct usb_gadget_driver *gadget_driver; ++ ++ void __iomem *regs; ++ size_t regs_size; ++ ++ int irq; ++ ++ u32 num_event_buffers; ++ u32 u1u2; ++ u32 maximum_speed; ++ u32 revision; ++ u32 mode; ++ ++#define DWC3_REVISION_173A 0x5533173a ++#define DWC3_REVISION_175A 0x5533175a ++#define DWC3_REVISION_180A 0x5533180a ++#define DWC3_REVISION_183A 0x5533183a ++#define DWC3_REVISION_185A 0x5533185a ++#define DWC3_REVISION_188A 0x5533188a ++#define DWC3_REVISION_190A 0x5533190a ++ ++ unsigned is_selfpowered:1; ++ unsigned three_stage_setup:1; ++ unsigned ep0_bounced:1; ++ unsigned ep0_expect_in:1; ++ unsigned start_config_issued:1; ++ unsigned setup_packet_pending:1; ++ unsigned delayed_status:1; ++ ++ enum dwc3_ep0_next ep0_next_event; ++ enum dwc3_ep0_state ep0state; ++ enum dwc3_link_state link_state; ++ enum dwc3_device_state dev_state; ++ ++ u8 speed; ++ void *mem; ++ ++ struct dwc3_hwparams hwparams; ++ struct dentry *root; ++}; ++ ++/* -------------------------------------------------------------------------- */ ++ ++#define DWC3_TRBSTS_OK 0 ++#define DWC3_TRBSTS_MISSED_ISOC 1 ++#define DWC3_TRBSTS_SETUP_PENDING 2 ++ ++#define DWC3_TRBCTL_NORMAL 1 ++#define DWC3_TRBCTL_CONTROL_SETUP 2 ++#define DWC3_TRBCTL_CONTROL_STATUS2 3 ++#define DWC3_TRBCTL_CONTROL_STATUS3 4 ++#define DWC3_TRBCTL_CONTROL_DATA 5 ++#define DWC3_TRBCTL_ISOCHRONOUS_FIRST 6 ++#define DWC3_TRBCTL_ISOCHRONOUS 7 ++#define DWC3_TRBCTL_LINK_TRB 8 ++ ++/* -------------------------------------------------------------------------- */ ++ ++struct dwc3_event_type { ++ u32 is_devspec:1; ++ u32 type:6; ++ u32 reserved8_31:25; ++} __packed; ++ ++#define DWC3_DEPEVT_XFERCOMPLETE 0x01 ++#define DWC3_DEPEVT_XFERINPROGRESS 0x02 ++#define DWC3_DEPEVT_XFERNOTREADY 0x03 ++#define DWC3_DEPEVT_RXTXFIFOEVT 0x04 ++#define DWC3_DEPEVT_STREAMEVT 0x06 ++#define DWC3_DEPEVT_EPCMDCMPLT 0x07 ++ ++/** ++ * struct dwc3_event_depvt - Device Endpoint Events ++ * @one_bit: indicates this is an endpoint event (not used) ++ * @endpoint_number: number of the endpoint ++ * @endpoint_event: The event we have: ++ * 0x00 - Reserved ++ * 0x01 - XferComplete ++ * 0x02 - XferInProgress ++ * 0x03 - XferNotReady ++ * 0x04 - RxTxFifoEvt (IN->Underrun, OUT->Overrun) ++ * 0x05 - Reserved ++ * 0x06 - StreamEvt ++ * 0x07 - EPCmdCmplt ++ * @reserved11_10: Reserved, don't use. ++ * @status: Indicates the status of the event. Refer to databook for ++ * more information. ++ * @parameters: Parameters of the current event. Refer to databook for ++ * more information. ++ */ ++struct dwc3_event_depevt { ++ u32 one_bit:1; ++ u32 endpoint_number:5; ++ u32 endpoint_event:4; ++ u32 reserved11_10:2; ++ u32 status:4; ++#define DEPEVT_STATUS_BUSERR (1 << 0) ++#define DEPEVT_STATUS_SHORT (1 << 1) ++#define DEPEVT_STATUS_IOC (1 << 2) ++#define DEPEVT_STATUS_LST (1 << 3) ++ ++/* Stream event only */ ++#define DEPEVT_STREAMEVT_FOUND 1 ++#define DEPEVT_STREAMEVT_NOTFOUND 2 ++ ++/* Control-only Status */ ++#define DEPEVT_STATUS_CONTROL_SETUP 0 ++#define DEPEVT_STATUS_CONTROL_DATA 1 ++#define DEPEVT_STATUS_CONTROL_STATUS 2 ++ ++ u32 parameters:16; ++} __packed; ++ ++/** ++ * struct dwc3_event_devt - Device Events ++ * @one_bit: indicates this is a non-endpoint event (not used) ++ * @device_event: indicates it's a device event. Should read as 0x00 ++ * @type: indicates the type of device event. ++ * 0 - DisconnEvt ++ * 1 - USBRst ++ * 2 - ConnectDone ++ * 3 - ULStChng ++ * 4 - WkUpEvt ++ * 5 - Reserved ++ * 6 - EOPF ++ * 7 - SOF ++ * 8 - Reserved ++ * 9 - ErrticErr ++ * 10 - CmdCmplt ++ * 11 - EvntOverflow ++ * 12 - VndrDevTstRcved ++ * @reserved15_12: Reserved, not used ++ * @event_info: Information about this event ++ * @reserved31_24: Reserved, not used ++ */ ++struct dwc3_event_devt { ++ u32 one_bit:1; ++ u32 device_event:7; ++ u32 type:4; ++ u32 reserved15_12:4; ++ u32 event_info:8; ++ u32 reserved31_24:8; ++} __packed; ++ ++/** ++ * struct dwc3_event_gevt - Other Core Events ++ * @one_bit: indicates this is a non-endpoint event (not used) ++ * @device_event: indicates it's (0x03) Carkit or (0x04) I2C event. ++ * @phy_port_number: self-explanatory ++ * @reserved31_12: Reserved, not used. ++ */ ++struct dwc3_event_gevt { ++ u32 one_bit:1; ++ u32 device_event:7; ++ u32 phy_port_number:4; ++ u32 reserved31_12:20; ++} __packed; ++ ++/** ++ * union dwc3_event - representation of Event Buffer contents ++ * @raw: raw 32-bit event ++ * @type: the type of the event ++ * @depevt: Device Endpoint Event ++ * @devt: Device Event ++ * @gevt: Global Event ++ */ ++union dwc3_event { ++ u32 raw; ++ struct dwc3_event_type type; ++ struct dwc3_event_depevt depevt; ++ struct dwc3_event_devt devt; ++ struct dwc3_event_gevt gevt; ++}; ++ ++/* ++ * DWC3 Features to be used as Driver Data ++ */ ++ ++#define DWC3_HAS_PERIPHERAL BIT(0) ++#define DWC3_HAS_XHCI BIT(1) ++#define DWC3_HAS_OTG BIT(3) ++ ++/* prototypes */ ++void dwc3_set_mode(struct dwc3 *dwc, u32 mode); ++ ++int dwc3_host_init(struct dwc3 *dwc); ++static inline void dwc3_host_exit(struct dwc3 *dwc) {} ++ ++int dwc3_gadget_init(struct dwc3 *dwc); ++void dwc3_gadget_exit(struct dwc3 *dwc); ++ ++extern int dwc3_get_device_id(void); ++extern void dwc3_put_device_id(int id); ++ ++#endif /* __DRIVERS_USB_DWC3_CORE_H */ +diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h +new file mode 100644 +index 0000000..52d0227 +--- /dev/null ++++ b/drivers/usb/dwc3/debug.h +@@ -0,0 +1,49 @@ ++/** ++ * debug.h - DesignWare USB3 DRD Controller Debug Header ++ * ++ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com ++ * ++ * Authors: Felipe Balbi , ++ * Sebastian Andrzej Siewior ++ * ++ * 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 the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not 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. ++ * ++ * 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 MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. ++ */ ++ ++#include "core.h" ++ ++#ifdef CONFIG_DEBUG_FS ++extern int dwc3_debugfs_init(struct dwc3 *); ++extern void dwc3_debugfs_exit(struct dwc3 *); ++#else ++static inline int dwc3_debugfs_init(struct dwc3 *d) ++{ return 0; } ++static inline void dwc3_debugfs_exit(struct dwc3 *d) ++{ } ++#endif +diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c +new file mode 100644 +index 0000000..97b2c40 +--- /dev/null ++++ b/drivers/usb/dwc3/debugfs.c +@@ -0,0 +1,521 @@ ++/** ++ * debugfs.c - DesignWare USB3 DRD Controller DebugFS file ++ * ++ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com ++ * ++ * Authors: Felipe Balbi , ++ * Sebastian Andrzej Siewior ++ * ++ * 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 the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not 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. ++ * ++ * 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 MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. ++ */ ++ ++#include ++#ifdef CONFIG_DWC3_off ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "core.h" ++#include "gadget.h" ++#include "io.h" ++#include "debug.h" ++ ++struct dwc3_register { ++ const char *name; ++ u32 offset; ++}; ++ ++#define dump_register(nm) \ ++{ \ ++ .name = __stringify(nm), \ ++ .offset = DWC3_ ##nm, \ ++} ++ ++static const struct dwc3_register dwc3_regs[] = { ++ dump_register(GSBUSCFG0), ++ dump_register(GSBUSCFG1), ++ dump_register(GTXTHRCFG), ++ dump_register(GRXTHRCFG), ++ dump_register(GCTL), ++ dump_register(GEVTEN), ++ dump_register(GSTS), ++ dump_register(GSNPSID), ++ dump_register(GGPIO), ++ dump_register(GUID), ++ dump_register(GUCTL), ++ dump_register(GBUSERRADDR0), ++ dump_register(GBUSERRADDR1), ++ dump_register(GPRTBIMAP0), ++ dump_register(GPRTBIMAP1), ++ dump_register(GHWPARAMS0), ++ dump_register(GHWPARAMS1), ++ dump_register(GHWPARAMS2), ++ dump_register(GHWPARAMS3), ++ dump_register(GHWPARAMS4), ++ dump_register(GHWPARAMS5), ++ dump_register(GHWPARAMS6), ++ dump_register(GHWPARAMS7), ++ dump_register(GDBGFIFOSPACE), ++ dump_register(GDBGLTSSM), ++ dump_register(GPRTBIMAP_HS0), ++ dump_register(GPRTBIMAP_HS1), ++ dump_register(GPRTBIMAP_FS0), ++ dump_register(GPRTBIMAP_FS1), ++ ++ dump_register(GUSB2PHYCFG(0)), ++ dump_register(GUSB2PHYCFG(1)), ++ dump_register(GUSB2PHYCFG(2)), ++ dump_register(GUSB2PHYCFG(3)), ++ dump_register(GUSB2PHYCFG(4)), ++ dump_register(GUSB2PHYCFG(5)), ++ dump_register(GUSB2PHYCFG(6)), ++ dump_register(GUSB2PHYCFG(7)), ++ dump_register(GUSB2PHYCFG(8)), ++ dump_register(GUSB2PHYCFG(9)), ++ dump_register(GUSB2PHYCFG(10)), ++ dump_register(GUSB2PHYCFG(11)), ++ dump_register(GUSB2PHYCFG(12)), ++ dump_register(GUSB2PHYCFG(13)), ++ dump_register(GUSB2PHYCFG(14)), ++ dump_register(GUSB2PHYCFG(15)), ++ ++ dump_register(GUSB2I2CCTL(0)), ++ dump_register(GUSB2I2CCTL(1)), ++ dump_register(GUSB2I2CCTL(2)), ++ dump_register(GUSB2I2CCTL(3)), ++ dump_register(GUSB2I2CCTL(4)), ++ dump_register(GUSB2I2CCTL(5)), ++ dump_register(GUSB2I2CCTL(6)), ++ dump_register(GUSB2I2CCTL(7)), ++ dump_register(GUSB2I2CCTL(8)), ++ dump_register(GUSB2I2CCTL(9)), ++ dump_register(GUSB2I2CCTL(10)), ++ dump_register(GUSB2I2CCTL(11)), ++ dump_register(GUSB2I2CCTL(12)), ++ dump_register(GUSB2I2CCTL(13)), ++ dump_register(GUSB2I2CCTL(14)), ++ dump_register(GUSB2I2CCTL(15)), ++ ++ dump_register(GUSB2PHYACC(0)), ++ dump_register(GUSB2PHYACC(1)), ++ dump_register(GUSB2PHYACC(2)), ++ dump_register(GUSB2PHYACC(3)), ++ dump_register(GUSB2PHYACC(4)), ++ dump_register(GUSB2PHYACC(5)), ++ dump_register(GUSB2PHYACC(6)), ++ dump_register(GUSB2PHYACC(7)), ++ dump_register(GUSB2PHYACC(8)), ++ dump_register(GUSB2PHYACC(9)), ++ dump_register(GUSB2PHYACC(10)), ++ dump_register(GUSB2PHYACC(11)), ++ dump_register(GUSB2PHYACC(12)), ++ dump_register(GUSB2PHYACC(13)), ++ dump_register(GUSB2PHYACC(14)), ++ dump_register(GUSB2PHYACC(15)), ++ ++ dump_register(GUSB3PIPECTL(0)), ++ dump_register(GUSB3PIPECTL(1)), ++ dump_register(GUSB3PIPECTL(2)), ++ dump_register(GUSB3PIPECTL(3)), ++ dump_register(GUSB3PIPECTL(4)), ++ dump_register(GUSB3PIPECTL(5)), ++ dump_register(GUSB3PIPECTL(6)), ++ dump_register(GUSB3PIPECTL(7)), ++ dump_register(GUSB3PIPECTL(8)), ++ dump_register(GUSB3PIPECTL(9)), ++ dump_register(GUSB3PIPECTL(10)), ++ dump_register(GUSB3PIPECTL(11)), ++ dump_register(GUSB3PIPECTL(12)), ++ dump_register(GUSB3PIPECTL(13)), ++ dump_register(GUSB3PIPECTL(14)), ++ dump_register(GUSB3PIPECTL(15)), ++ ++ dump_register(GTXFIFOSIZ(0)), ++ dump_register(GTXFIFOSIZ(1)), ++ dump_register(GTXFIFOSIZ(2)), ++ dump_register(GTXFIFOSIZ(3)), ++ dump_register(GTXFIFOSIZ(4)), ++ dump_register(GTXFIFOSIZ(5)), ++ dump_register(GTXFIFOSIZ(6)), ++ dump_register(GTXFIFOSIZ(7)), ++ dump_register(GTXFIFOSIZ(8)), ++ dump_register(GTXFIFOSIZ(9)), ++ dump_register(GTXFIFOSIZ(10)), ++ dump_register(GTXFIFOSIZ(11)), ++ dump_register(GTXFIFOSIZ(12)), ++ dump_register(GTXFIFOSIZ(13)), ++ dump_register(GTXFIFOSIZ(14)), ++ dump_register(GTXFIFOSIZ(15)), ++ dump_register(GTXFIFOSIZ(16)), ++ dump_register(GTXFIFOSIZ(17)), ++ dump_register(GTXFIFOSIZ(18)), ++ dump_register(GTXFIFOSIZ(19)), ++ dump_register(GTXFIFOSIZ(20)), ++ dump_register(GTXFIFOSIZ(21)), ++ dump_register(GTXFIFOSIZ(22)), ++ dump_register(GTXFIFOSIZ(23)), ++ dump_register(GTXFIFOSIZ(24)), ++ dump_register(GTXFIFOSIZ(25)), ++ dump_register(GTXFIFOSIZ(26)), ++ dump_register(GTXFIFOSIZ(27)), ++ dump_register(GTXFIFOSIZ(28)), ++ dump_register(GTXFIFOSIZ(29)), ++ dump_register(GTXFIFOSIZ(30)), ++ dump_register(GTXFIFOSIZ(31)), ++ ++ dump_register(GRXFIFOSIZ(0)), ++ dump_register(GRXFIFOSIZ(1)), ++ dump_register(GRXFIFOSIZ(2)), ++ dump_register(GRXFIFOSIZ(3)), ++ dump_register(GRXFIFOSIZ(4)), ++ dump_register(GRXFIFOSIZ(5)), ++ dump_register(GRXFIFOSIZ(6)), ++ dump_register(GRXFIFOSIZ(7)), ++ dump_register(GRXFIFOSIZ(8)), ++ dump_register(GRXFIFOSIZ(9)), ++ dump_register(GRXFIFOSIZ(10)), ++ dump_register(GRXFIFOSIZ(11)), ++ dump_register(GRXFIFOSIZ(12)), ++ dump_register(GRXFIFOSIZ(13)), ++ dump_register(GRXFIFOSIZ(14)), ++ dump_register(GRXFIFOSIZ(15)), ++ dump_register(GRXFIFOSIZ(16)), ++ dump_register(GRXFIFOSIZ(17)), ++ dump_register(GRXFIFOSIZ(18)), ++ dump_register(GRXFIFOSIZ(19)), ++ dump_register(GRXFIFOSIZ(20)), ++ dump_register(GRXFIFOSIZ(21)), ++ dump_register(GRXFIFOSIZ(22)), ++ dump_register(GRXFIFOSIZ(23)), ++ dump_register(GRXFIFOSIZ(24)), ++ dump_register(GRXFIFOSIZ(25)), ++ dump_register(GRXFIFOSIZ(26)), ++ dump_register(GRXFIFOSIZ(27)), ++ dump_register(GRXFIFOSIZ(28)), ++ dump_register(GRXFIFOSIZ(29)), ++ dump_register(GRXFIFOSIZ(30)), ++ dump_register(GRXFIFOSIZ(31)), ++ ++ dump_register(GEVNTADRLO(0)), ++ dump_register(GEVNTADRHI(0)), ++ dump_register(GEVNTSIZ(0)), ++ dump_register(GEVNTCOUNT(0)), ++ ++ dump_register(GHWPARAMS8), ++ dump_register(DCFG), ++ dump_register(DCTL), ++ dump_register(DEVTEN), ++ dump_register(DSTS), ++ dump_register(DGCMDPAR), ++ dump_register(DGCMD), ++ dump_register(DALEPENA), ++ ++ dump_register(DEPCMDPAR2(0)), ++ dump_register(DEPCMDPAR2(1)), ++ dump_register(DEPCMDPAR2(2)), ++ dump_register(DEPCMDPAR2(3)), ++ dump_register(DEPCMDPAR2(4)), ++ dump_register(DEPCMDPAR2(5)), ++ dump_register(DEPCMDPAR2(6)), ++ dump_register(DEPCMDPAR2(7)), ++ dump_register(DEPCMDPAR2(8)), ++ dump_register(DEPCMDPAR2(9)), ++ dump_register(DEPCMDPAR2(10)), ++ dump_register(DEPCMDPAR2(11)), ++ dump_register(DEPCMDPAR2(12)), ++ dump_register(DEPCMDPAR2(13)), ++ dump_register(DEPCMDPAR2(14)), ++ dump_register(DEPCMDPAR2(15)), ++ dump_register(DEPCMDPAR2(16)), ++ dump_register(DEPCMDPAR2(17)), ++ dump_register(DEPCMDPAR2(18)), ++ dump_register(DEPCMDPAR2(19)), ++ dump_register(DEPCMDPAR2(20)), ++ dump_register(DEPCMDPAR2(21)), ++ dump_register(DEPCMDPAR2(22)), ++ dump_register(DEPCMDPAR2(23)), ++ dump_register(DEPCMDPAR2(24)), ++ dump_register(DEPCMDPAR2(25)), ++ dump_register(DEPCMDPAR2(26)), ++ dump_register(DEPCMDPAR2(27)), ++ dump_register(DEPCMDPAR2(28)), ++ dump_register(DEPCMDPAR2(29)), ++ dump_register(DEPCMDPAR2(30)), ++ dump_register(DEPCMDPAR2(31)), ++ ++ dump_register(DEPCMDPAR1(0)), ++ dump_register(DEPCMDPAR1(1)), ++ dump_register(DEPCMDPAR1(2)), ++ dump_register(DEPCMDPAR1(3)), ++ dump_register(DEPCMDPAR1(4)), ++ dump_register(DEPCMDPAR1(5)), ++ dump_register(DEPCMDPAR1(6)), ++ dump_register(DEPCMDPAR1(7)), ++ dump_register(DEPCMDPAR1(8)), ++ dump_register(DEPCMDPAR1(9)), ++ dump_register(DEPCMDPAR1(10)), ++ dump_register(DEPCMDPAR1(11)), ++ dump_register(DEPCMDPAR1(12)), ++ dump_register(DEPCMDPAR1(13)), ++ dump_register(DEPCMDPAR1(14)), ++ dump_register(DEPCMDPAR1(15)), ++ dump_register(DEPCMDPAR1(16)), ++ dump_register(DEPCMDPAR1(17)), ++ dump_register(DEPCMDPAR1(18)), ++ dump_register(DEPCMDPAR1(19)), ++ dump_register(DEPCMDPAR1(20)), ++ dump_register(DEPCMDPAR1(21)), ++ dump_register(DEPCMDPAR1(22)), ++ dump_register(DEPCMDPAR1(23)), ++ dump_register(DEPCMDPAR1(24)), ++ dump_register(DEPCMDPAR1(25)), ++ dump_register(DEPCMDPAR1(26)), ++ dump_register(DEPCMDPAR1(27)), ++ dump_register(DEPCMDPAR1(28)), ++ dump_register(DEPCMDPAR1(29)), ++ dump_register(DEPCMDPAR1(30)), ++ dump_register(DEPCMDPAR1(31)), ++ ++ dump_register(DEPCMDPAR0(0)), ++ dump_register(DEPCMDPAR0(1)), ++ dump_register(DEPCMDPAR0(2)), ++ dump_register(DEPCMDPAR0(3)), ++ dump_register(DEPCMDPAR0(4)), ++ dump_register(DEPCMDPAR0(5)), ++ dump_register(DEPCMDPAR0(6)), ++ dump_register(DEPCMDPAR0(7)), ++ dump_register(DEPCMDPAR0(8)), ++ dump_register(DEPCMDPAR0(9)), ++ dump_register(DEPCMDPAR0(10)), ++ dump_register(DEPCMDPAR0(11)), ++ dump_register(DEPCMDPAR0(12)), ++ dump_register(DEPCMDPAR0(13)), ++ dump_register(DEPCMDPAR0(14)), ++ dump_register(DEPCMDPAR0(15)), ++ dump_register(DEPCMDPAR0(16)), ++ dump_register(DEPCMDPAR0(17)), ++ dump_register(DEPCMDPAR0(18)), ++ dump_register(DEPCMDPAR0(19)), ++ dump_register(DEPCMDPAR0(20)), ++ dump_register(DEPCMDPAR0(21)), ++ dump_register(DEPCMDPAR0(22)), ++ dump_register(DEPCMDPAR0(23)), ++ dump_register(DEPCMDPAR0(24)), ++ dump_register(DEPCMDPAR0(25)), ++ dump_register(DEPCMDPAR0(26)), ++ dump_register(DEPCMDPAR0(27)), ++ dump_register(DEPCMDPAR0(28)), ++ dump_register(DEPCMDPAR0(29)), ++ dump_register(DEPCMDPAR0(30)), ++ dump_register(DEPCMDPAR0(31)), ++ ++ dump_register(DEPCMD(0)), ++ dump_register(DEPCMD(1)), ++ dump_register(DEPCMD(2)), ++ dump_register(DEPCMD(3)), ++ dump_register(DEPCMD(4)), ++ dump_register(DEPCMD(5)), ++ dump_register(DEPCMD(6)), ++ dump_register(DEPCMD(7)), ++ dump_register(DEPCMD(8)), ++ dump_register(DEPCMD(9)), ++ dump_register(DEPCMD(10)), ++ dump_register(DEPCMD(11)), ++ dump_register(DEPCMD(12)), ++ dump_register(DEPCMD(13)), ++ dump_register(DEPCMD(14)), ++ dump_register(DEPCMD(15)), ++ dump_register(DEPCMD(16)), ++ dump_register(DEPCMD(17)), ++ dump_register(DEPCMD(18)), ++ dump_register(DEPCMD(19)), ++ dump_register(DEPCMD(20)), ++ dump_register(DEPCMD(21)), ++ dump_register(DEPCMD(22)), ++ dump_register(DEPCMD(23)), ++ dump_register(DEPCMD(24)), ++ dump_register(DEPCMD(25)), ++ dump_register(DEPCMD(26)), ++ dump_register(DEPCMD(27)), ++ dump_register(DEPCMD(28)), ++ dump_register(DEPCMD(29)), ++ dump_register(DEPCMD(30)), ++ dump_register(DEPCMD(31)), ++ ++ dump_register(OCFG), ++ dump_register(OCTL), ++ dump_register(OEVTEN), ++ dump_register(OSTS), ++}; ++ ++static int dwc3_regdump_show(struct seq_file *s, void *unused) ++{ ++ struct dwc3 *dwc = s->private; ++ int i; ++ ++ seq_printf(s, "DesignWare USB3 Core Register Dump\n"); ++ ++ for (i = 0; i < ARRAY_SIZE(dwc3_regs); i++) { ++ seq_printf(s, "%-20s : %08x\n", dwc3_regs[i].name, ++ dwc3_readl(dwc->regs, dwc3_regs[i].offset)); ++ } ++ ++ return 0; ++} ++ ++static int dwc3_regdump_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, dwc3_regdump_show, inode->i_private); ++} ++ ++static const struct file_operations dwc3_regdump_fops = { ++ .open = dwc3_regdump_open, ++ .read = seq_read, ++ .release = single_release, ++}; ++ ++static int dwc3_mode_show(struct seq_file *s, void *unused) ++{ ++ struct dwc3 *dwc = s->private; ++ unsigned long flags; ++ u32 reg; ++ ++ spin_lock_irqsave(&dwc->lock, flags); ++ reg = dwc3_readl(dwc->regs, DWC3_GCTL); ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ ++ switch (DWC3_GCTL_PRTCAP(reg)) { ++ case DWC3_GCTL_PRTCAP_HOST: ++ seq_printf(s, "host\n"); ++ break; ++ case DWC3_GCTL_PRTCAP_DEVICE: ++ seq_printf(s, "device\n"); ++ break; ++ case DWC3_GCTL_PRTCAP_OTG: ++ seq_printf(s, "OTG\n"); ++ break; ++ default: ++ seq_printf(s, "UNKNOWN %08x\n", DWC3_GCTL_PRTCAP(reg)); ++ } ++ ++ return 0; ++} ++ ++static int dwc3_mode_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, dwc3_mode_show, inode->i_private); ++} ++ ++static ssize_t dwc3_mode_write(struct file *file, ++ const char __user *ubuf, size_t count, loff_t *ppos) ++{ ++ struct seq_file *s = file->private_data; ++ struct dwc3 *dwc = s->private; ++ unsigned long flags; ++ u32 mode = 0; ++ char buf[32]; ++ ++ if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) ++ return -EFAULT; ++ ++ if (!strncmp(buf, "host", 4)) ++ mode |= DWC3_GCTL_PRTCAP_HOST; ++ ++ if (!strncmp(buf, "device", 6)) ++ mode |= DWC3_GCTL_PRTCAP_DEVICE; ++ ++ if (!strncmp(buf, "otg", 3)) ++ mode |= DWC3_GCTL_PRTCAP_OTG; ++ ++ if (mode) { ++ spin_lock_irqsave(&dwc->lock, flags); ++ dwc3_set_mode(dwc, mode); ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ } ++ return count; ++} ++ ++static const struct file_operations dwc3_mode_fops = { ++ .open = dwc3_mode_open, ++ .write = dwc3_mode_write, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++int __devinit dwc3_debugfs_init(struct dwc3 *dwc) ++{ ++ struct dentry *root; ++ struct dentry *file; ++ int ret; ++ ++ root = debugfs_create_dir(dev_name(dwc->dev), NULL); ++ if (IS_ERR(root)) { ++ ret = PTR_ERR(root); ++ goto err0; ++ } ++ ++ dwc->root = root; ++ ++ file = debugfs_create_file("regdump", S_IRUGO, root, dwc, ++ &dwc3_regdump_fops); ++ if (IS_ERR(file)) { ++ ret = PTR_ERR(file); ++ goto err1; ++ } ++ ++ file = debugfs_create_file("mode", S_IRUGO | S_IWUSR, root, ++ dwc, &dwc3_mode_fops); ++ if (IS_ERR(file)) { ++ ret = PTR_ERR(file); ++ goto err1; ++ } ++ ++ return 0; ++ ++err1: ++ debugfs_remove_recursive(root); ++ ++err0: ++ return ret; ++} ++ ++void __devexit dwc3_debugfs_exit(struct dwc3 *dwc) ++{ ++ debugfs_remove_recursive(dwc->root); ++ dwc->root = NULL; ++} ++#endif +diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c +new file mode 100644 +index 0000000..11307c2 +--- /dev/null ++++ b/drivers/usb/dwc3/dwc3-omap.c +@@ -0,0 +1,418 @@ ++/** ++ * dwc3-omap.c - OMAP Specific Glue layer ++ * ++ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com ++ * ++ * Authors: Felipe Balbi , ++ * Sebastian Andrzej Siewior ++ * ++ * 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 the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not 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. ++ * ++ * 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 MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. ++ */ ++ ++#include ++#ifdef CONFIG_DWC3_off ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "core.h" ++#include "io.h" ++ ++/* ++ * All these registers belong to OMAP's Wrapper around the ++ * DesignWare USB3 Core. ++ */ ++ ++#define USBOTGSS_REVISION 0x0000 ++#define USBOTGSS_SYSCONFIG 0x0010 ++#define USBOTGSS_IRQ_EOI 0x0020 ++#define USBOTGSS_IRQSTATUS_RAW_0 0x0024 ++#define USBOTGSS_IRQSTATUS_0 0x0028 ++#define USBOTGSS_IRQENABLE_SET_0 0x002c ++#define USBOTGSS_IRQENABLE_CLR_0 0x0030 ++#define USBOTGSS_IRQSTATUS_RAW_1 0x0034 ++#define USBOTGSS_IRQSTATUS_1 0x0038 ++#define USBOTGSS_IRQENABLE_SET_1 0x003c ++#define USBOTGSS_IRQENABLE_CLR_1 0x0040 ++#define USBOTGSS_UTMI_OTG_CTRL 0x0080 ++#define USBOTGSS_UTMI_OTG_STATUS 0x0084 ++#define USBOTGSS_MMRAM_OFFSET 0x0100 ++#define USBOTGSS_FLADJ 0x0104 ++#define USBOTGSS_DEBUG_CFG 0x0108 ++#define USBOTGSS_DEBUG_DATA 0x010c ++ ++/* SYSCONFIG REGISTER */ ++#define USBOTGSS_SYSCONFIG_DMADISABLE (1 << 16) ++#define USBOTGSS_SYSCONFIG_STANDBYMODE(x) ((x) << 4) ++ ++#define USBOTGSS_STANDBYMODE_FORCE_STANDBY 0 ++#define USBOTGSS_STANDBYMODE_NO_STANDBY 1 ++#define USBOTGSS_STANDBYMODE_SMART_STANDBY 2 ++#define USBOTGSS_STANDBYMODE_SMART_WAKEUP 3 ++ ++#define USBOTGSS_STANDBYMODE_MASK (0x03 << 4) ++ ++#define USBOTGSS_SYSCONFIG_IDLEMODE(x) ((x) << 2) ++ ++#define USBOTGSS_IDLEMODE_FORCE_IDLE 0 ++#define USBOTGSS_IDLEMODE_NO_IDLE 1 ++#define USBOTGSS_IDLEMODE_SMART_IDLE 2 ++#define USBOTGSS_IDLEMODE_SMART_WAKEUP 3 ++ ++#define USBOTGSS_IDLEMODE_MASK (0x03 << 2) ++ ++/* IRQ_EOI REGISTER */ ++#define USBOTGSS_IRQ_EOI_LINE_NUMBER (1 << 0) ++ ++/* IRQS0 BITS */ ++#define USBOTGSS_IRQO_COREIRQ_ST (1 << 0) ++ ++/* IRQ1 BITS */ ++#define USBOTGSS_IRQ1_DMADISABLECLR (1 << 17) ++#define USBOTGSS_IRQ1_OEVT (1 << 16) ++#define USBOTGSS_IRQ1_DRVVBUS_RISE (1 << 13) ++#define USBOTGSS_IRQ1_CHRGVBUS_RISE (1 << 12) ++#define USBOTGSS_IRQ1_DISCHRGVBUS_RISE (1 << 11) ++#define USBOTGSS_IRQ1_IDPULLUP_RISE (1 << 8) ++#define USBOTGSS_IRQ1_DRVVBUS_FALL (1 << 5) ++#define USBOTGSS_IRQ1_CHRGVBUS_FALL (1 << 4) ++#define USBOTGSS_IRQ1_DISCHRGVBUS_FALL (1 << 3) ++#define USBOTGSS_IRQ1_IDPULLUP_FALL (1 << 0) ++ ++/* UTMI_OTG_CTRL REGISTER */ ++#define USBOTGSS_UTMI_OTG_CTRL_DRVVBUS (1 << 5) ++#define USBOTGSS_UTMI_OTG_CTRL_CHRGVBUS (1 << 4) ++#define USBOTGSS_UTMI_OTG_CTRL_DISCHRGVBUS (1 << 3) ++#define USBOTGSS_UTMI_OTG_CTRL_IDPULLUP (1 << 0) ++ ++/* UTMI_OTG_STATUS REGISTER */ ++#define USBOTGSS_UTMI_OTG_STATUS_SW_MODE (1 << 31) ++#define USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT (1 << 9) ++#define USBOTGSS_UTMI_OTG_STATUS_TXBITSTUFFENABLE (1 << 8) ++#define USBOTGSS_UTMI_OTG_STATUS_IDDIG (1 << 4) ++#define USBOTGSS_UTMI_OTG_STATUS_SESSEND (1 << 3) ++#define USBOTGSS_UTMI_OTG_STATUS_SESSVALID (1 << 2) ++#define USBOTGSS_UTMI_OTG_STATUS_VBUSVALID (1 << 1) ++ ++struct dwc3_omap { ++ /* device lock */ ++ spinlock_t lock; ++ ++ struct platform_device *dwc3; ++ struct device *dev; ++ ++ int irq; ++ void __iomem *base; ++ ++ void *context; ++ u32 resource_size; ++ ++ u32 dma_status:1; ++}; ++ ++static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap) ++{ ++ struct dwc3_omap *omap = _omap; ++ u32 reg; ++ ++ spin_lock(&omap->lock); ++ ++ reg = dwc3_readl(omap->base, USBOTGSS_IRQSTATUS_1); ++ ++ if (reg & USBOTGSS_IRQ1_DMADISABLECLR) { ++ dev_dbg(omap->dev, "DMA Disable was Cleared\n"); ++ omap->dma_status = false; ++ } ++ ++ if (reg & USBOTGSS_IRQ1_OEVT) ++ dev_dbg(omap->dev, "OTG Event\n"); ++ ++ if (reg & USBOTGSS_IRQ1_DRVVBUS_RISE) ++ dev_dbg(omap->dev, "DRVVBUS Rise\n"); ++ ++ if (reg & USBOTGSS_IRQ1_CHRGVBUS_RISE) ++ dev_dbg(omap->dev, "CHRGVBUS Rise\n"); ++ ++ if (reg & USBOTGSS_IRQ1_DISCHRGVBUS_RISE) ++ dev_dbg(omap->dev, "DISCHRGVBUS Rise\n"); ++ ++ if (reg & USBOTGSS_IRQ1_IDPULLUP_RISE) ++ dev_dbg(omap->dev, "IDPULLUP Rise\n"); ++ ++ if (reg & USBOTGSS_IRQ1_DRVVBUS_FALL) ++ dev_dbg(omap->dev, "DRVVBUS Fall\n"); ++ ++ if (reg & USBOTGSS_IRQ1_CHRGVBUS_FALL) ++ dev_dbg(omap->dev, "CHRGVBUS Fall\n"); ++ ++ if (reg & USBOTGSS_IRQ1_DISCHRGVBUS_FALL) ++ dev_dbg(omap->dev, "DISCHRGVBUS Fall\n"); ++ ++ if (reg & USBOTGSS_IRQ1_IDPULLUP_FALL) ++ dev_dbg(omap->dev, "IDPULLUP Fall\n"); ++ ++ dwc3_writel(omap->base, USBOTGSS_IRQSTATUS_1, reg); ++ ++ reg = dwc3_readl(omap->base, USBOTGSS_IRQSTATUS_0); ++ dwc3_writel(omap->base, USBOTGSS_IRQSTATUS_0, reg); ++ ++ spin_unlock(&omap->lock); ++ ++ return IRQ_HANDLED; ++} ++ ++static int __devinit dwc3_omap_probe(struct platform_device *pdev) ++{ ++ struct dwc3_omap_data *pdata = pdev->dev.platform_data; ++ struct platform_device *dwc3; ++ struct dwc3_omap *omap; ++ struct resource *res; ++ ++ int devid; ++ int ret = -ENOMEM; ++ int irq; ++ ++ u32 reg; ++ ++ void __iomem *base; ++ void *context; ++ ++ omap = kzalloc(sizeof(*omap), GFP_KERNEL); ++ if (!omap) { ++ dev_err(&pdev->dev, "not enough memory\n"); ++ goto err0; ++ } ++ ++ platform_set_drvdata(pdev, omap); ++ ++ irq = platform_get_irq(pdev, 1); ++ if (irq < 0) { ++ dev_err(&pdev->dev, "missing IRQ resource\n"); ++ ret = -EINVAL; ++ goto err1; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ if (!res) { ++ dev_err(&pdev->dev, "missing memory base resource\n"); ++ ret = -EINVAL; ++ goto err1; ++ } ++ ++ base = ioremap_nocache(res->start, resource_size(res)); ++ if (!base) { ++ dev_err(&pdev->dev, "ioremap failed\n"); ++ goto err1; ++ } ++ ++ devid = dwc3_get_device_id(); ++ if (devid < 0) ++ goto err2; ++ ++ dwc3 = platform_device_alloc("dwc3", devid); ++ if (!dwc3) { ++ dev_err(&pdev->dev, "couldn't allocate dwc3 device\n"); ++ goto err3; ++ } ++ ++ context = kzalloc(resource_size(res), GFP_KERNEL); ++ if (!context) { ++ dev_err(&pdev->dev, "couldn't allocate dwc3 context memory\n"); ++ goto err4; ++ } ++ ++ spin_lock_init(&omap->lock); ++ dma_set_coherent_mask(&dwc3->dev, pdev->dev.coherent_dma_mask); ++ ++ dwc3->dev.parent = &pdev->dev; ++ dwc3->dev.dma_mask = pdev->dev.dma_mask; ++ dwc3->dev.dma_parms = pdev->dev.dma_parms; ++ omap->resource_size = resource_size(res); ++ omap->context = context; ++ omap->dev = &pdev->dev; ++ omap->irq = irq; ++ omap->base = base; ++ omap->dwc3 = dwc3; ++ ++ reg = dwc3_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS); ++ ++ if (!pdata) { ++ dev_dbg(&pdev->dev, "missing platform data\n"); ++ } else { ++ switch (pdata->utmi_mode) { ++ case DWC3_OMAP_UTMI_MODE_SW: ++ reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE; ++ break; ++ case DWC3_OMAP_UTMI_MODE_HW: ++ reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE; ++ break; ++ default: ++ dev_dbg(&pdev->dev, "UNKNOWN utmi mode %d\n", ++ pdata->utmi_mode); ++ } ++ } ++ ++ dwc3_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, reg); ++ ++ /* check the DMA Status */ ++ reg = dwc3_readl(omap->base, USBOTGSS_SYSCONFIG); ++ omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE); ++ ++ /* Set No-Idle and No-Standby */ ++ reg &= ~(USBOTGSS_STANDBYMODE_MASK ++ | USBOTGSS_IDLEMODE_MASK); ++ ++ reg |= (USBOTGSS_SYSCONFIG_STANDBYMODE(USBOTGSS_STANDBYMODE_NO_STANDBY) ++ | USBOTGSS_SYSCONFIG_IDLEMODE(USBOTGSS_IDLEMODE_NO_IDLE)); ++ ++ dwc3_writel(omap->base, USBOTGSS_SYSCONFIG, reg); ++ ++ ret = request_irq(omap->irq, dwc3_omap_interrupt, 0, ++ "dwc3-omap", omap); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to request IRQ #%d --> %d\n", ++ omap->irq, ret); ++ goto err5; ++ } ++ ++ /* enable all IRQs */ ++ reg = USBOTGSS_IRQO_COREIRQ_ST; ++ dwc3_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, reg); ++ ++ reg = (USBOTGSS_IRQ1_OEVT | ++ USBOTGSS_IRQ1_DRVVBUS_RISE | ++ USBOTGSS_IRQ1_CHRGVBUS_RISE | ++ USBOTGSS_IRQ1_DISCHRGVBUS_RISE | ++ USBOTGSS_IRQ1_IDPULLUP_RISE | ++ USBOTGSS_IRQ1_DRVVBUS_FALL | ++ USBOTGSS_IRQ1_CHRGVBUS_FALL | ++ USBOTGSS_IRQ1_DISCHRGVBUS_FALL | ++ USBOTGSS_IRQ1_IDPULLUP_FALL); ++ ++ dwc3_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg); ++ ++ ret = platform_device_add_resources(dwc3, pdev->resource, ++ pdev->num_resources); ++ if (ret) { ++ dev_err(&pdev->dev, "couldn't add resources to dwc3 device\n"); ++ goto err6; ++ } ++ ++ ret = platform_device_add(dwc3); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to register dwc3 device\n"); ++ goto err6; ++ } ++ ++ return 0; ++ ++err6: ++ free_irq(omap->irq, omap); ++ ++err5: ++ kfree(omap->context); ++ ++err4: ++ platform_device_put(dwc3); ++ ++err3: ++ dwc3_put_device_id(devid); ++ ++err2: ++ iounmap(base); ++ ++err1: ++ kfree(omap); ++ ++err0: ++ return ret; ++} ++ ++static int __devexit dwc3_omap_remove(struct platform_device *pdev) ++{ ++ struct dwc3_omap *omap = platform_get_drvdata(pdev); ++ ++ platform_device_unregister(omap->dwc3); ++ ++ dwc3_put_device_id(omap->dwc3->id); ++ free_irq(omap->irq, omap); ++ iounmap(omap->base); ++ ++ kfree(omap->context); ++ kfree(omap); ++ ++ return 0; ++} ++ ++static const struct of_device_id of_dwc3_match[] = { ++ { ++ "ti,omap5430-dwc3" ++ }, ++ { ++ "ti,omap5-dwc3" ++ }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, of_dwc3_match); ++ ++static struct platform_driver dwc3_omap_driver = { ++ .probe = dwc3_omap_probe, ++ .remove = __devexit_p(dwc3_omap_remove), ++ .driver = { ++ .name = "omap-dwc3", ++ .of_match_table = of_dwc3_match, ++ }, ++}; ++ ++MODULE_ALIAS("platform:omap-dwc3"); ++MODULE_AUTHOR("Felipe Balbi "); ++MODULE_LICENSE("Dual BSD/GPL"); ++MODULE_DESCRIPTION("DesignWare USB3 OMAP Glue Layer"); ++ ++static int __init dwc3_omap_init(void) ++{ ++ return platform_driver_register(&dwc3_omap_driver); ++} ++module_init(dwc3_omap_init); ++ ++static void __exit dwc3_omap_exit(void) ++{ ++ platform_driver_unregister(&dwc3_omap_driver); ++} ++module_exit(dwc3_omap_exit); ++#endif +diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c +new file mode 100644 +index 0000000..dda702c +--- /dev/null ++++ b/drivers/usb/dwc3/dwc3-pci.c +@@ -0,0 +1,187 @@ ++/** ++ * dwc3-pci.c - PCI Specific glue layer ++ * ++ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com ++ * ++ * Authors: Felipe Balbi , ++ * Sebastian Andrzej Siewior ++ * ++ * 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 the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not 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. ++ * ++ * 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 MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. ++ */ ++ ++#include ++#ifdef CONFIG_DWC3_off ++#include ++#include ++#include ++#include ++#include ++ ++#include "core.h" ++ ++/* FIXME define these in */ ++#define PCI_VENDOR_ID_SYNOPSYS 0x16c3 ++#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd ++ ++struct dwc3_pci { ++ struct device *dev; ++ struct platform_device *dwc3; ++}; ++ ++static int __devinit dwc3_pci_probe(struct pci_dev *pci, ++ const struct pci_device_id *id) ++{ ++ struct resource res[2]; ++ struct platform_device *dwc3; ++ struct dwc3_pci *glue; ++ int ret = -ENOMEM; ++ int devid; ++ ++ glue = kzalloc(sizeof(*glue), GFP_KERNEL); ++ if (!glue) { ++ dev_err(&pci->dev, "not enough memory\n"); ++ goto err0; ++ } ++ ++ glue->dev = &pci->dev; ++ ++ ret = pci_enable_device(pci); ++ if (ret) { ++ dev_err(&pci->dev, "failed to enable pci device\n"); ++ goto err1; ++ } ++ ++ pci_set_power_state(pci, PCI_D0); ++ pci_set_master(pci); ++ ++ devid = dwc3_get_device_id(); ++ if (devid < 0) ++ goto err2; ++ ++ dwc3 = platform_device_alloc("dwc3", devid); ++ if (!dwc3) { ++ dev_err(&pci->dev, "couldn't allocate dwc3 device\n"); ++ goto err3; ++ } ++ ++ memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res)); ++ ++ res[0].start = pci_resource_start(pci, 0); ++ res[0].end = pci_resource_end(pci, 0); ++ res[0].name = "dwc_usb3"; ++ res[0].flags = IORESOURCE_MEM; ++ ++ res[1].start = pci->irq; ++ res[1].name = "dwc_usb3"; ++ res[1].flags = IORESOURCE_IRQ; ++ ++ ret = platform_device_add_resources(dwc3, res, ARRAY_SIZE(res)); ++ if (ret) { ++ dev_err(&pci->dev, "couldn't add resources to dwc3 device\n"); ++ goto err4; ++ } ++ ++ pci_set_drvdata(pci, glue); ++ ++ dma_set_coherent_mask(&dwc3->dev, pci->dev.coherent_dma_mask); ++ ++ dwc3->dev.dma_mask = pci->dev.dma_mask; ++ dwc3->dev.dma_parms = pci->dev.dma_parms; ++ dwc3->dev.parent = &pci->dev; ++ glue->dwc3 = dwc3; ++ ++ ret = platform_device_add(dwc3); ++ if (ret) { ++ dev_err(&pci->dev, "failed to register dwc3 device\n"); ++ goto err4; ++ } ++ ++ return 0; ++ ++err4: ++ pci_set_drvdata(pci, NULL); ++ platform_device_put(dwc3); ++ ++err3: ++ dwc3_put_device_id(devid); ++ ++err2: ++ pci_disable_device(pci); ++ ++err1: ++ kfree(pci); ++ ++err0: ++ return ret; ++} ++ ++static void __devexit dwc3_pci_remove(struct pci_dev *pci) ++{ ++ struct dwc3_pci *glue = pci_get_drvdata(pci); ++ ++ dwc3_put_device_id(glue->dwc3->id); ++ platform_device_unregister(glue->dwc3); ++ pci_set_drvdata(pci, NULL); ++ pci_disable_device(pci); ++ kfree(glue); ++} ++ ++static DEFINE_PCI_DEVICE_TABLE(dwc3_pci_id_table) = { ++ { ++ PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, ++ PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3), ++ }, ++ { } /* Terminating Entry */ ++}; ++MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table); ++ ++static struct pci_driver dwc3_pci_driver = { ++ .name = "dwc3-pci", ++ .id_table = dwc3_pci_id_table, ++ .probe = dwc3_pci_probe, ++ .remove = __devexit_p(dwc3_pci_remove), ++}; ++ ++MODULE_AUTHOR("Felipe Balbi "); ++MODULE_LICENSE("Dual BSD/GPL"); ++MODULE_DESCRIPTION("DesignWare USB3 PCI Glue Layer"); ++ ++static int __init dwc3_pci_init(void) ++{ ++ return pci_register_driver(&dwc3_pci_driver); ++} ++module_init(dwc3_pci_init); ++ ++static void __exit dwc3_pci_exit(void) ++{ ++ pci_unregister_driver(&dwc3_pci_driver); ++} ++module_exit(dwc3_pci_exit); ++#endif +diff --git a/drivers/usb/dwc3/dwc3_core.c b/drivers/usb/dwc3/dwc3_core.c +new file mode 100644 +index 0000000..26b6ff7 +--- /dev/null ++++ b/drivers/usb/dwc3/dwc3_core.c +@@ -0,0 +1,513 @@ ++/** ++ * core.c - DesignWare USB3 DRD Controller Core file ++ * ++ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com ++ * ++ * Authors: Felipe Balbi , ++ * Sebastian Andrzej Siewior ++ * ++ * 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 the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not 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. ++ * ++ * 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 MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. ++ */ ++ ++#include ++#include ++ ++#include ++#include ++ ++#include "core.h" ++#include "gadget.h" ++#include "io.h" ++ ++#include "debug.h" ++ ++static char *maximum_speed = "super"; ++/* -------------------------------------------------------------------------- */ ++ ++#define DWC3_DEVS_POSSIBLE 32 ++ ++ ++int dwc3_get_device_id(void) ++{ ++ static int dwc3_devs; ++ ++ return dwc3_devs++; ++} ++ ++void dwc3_put_device_id(int id) ++{ ++} ++ ++void dwc3_set_mode(struct dwc3 *dwc, u32 mode) ++{ ++ u32 reg; ++ ++ reg = dwc3_readl(dwc->regs, DWC3_GCTL); ++ reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG)); ++ reg |= DWC3_GCTL_PRTCAPDIR(mode); ++ dwc3_writel(dwc->regs, DWC3_GCTL, reg); ++} ++ ++/** ++ * dwc3_core_soft_reset - Issues core soft reset and PHY reset ++ * @dwc: pointer to our context structure ++ */ ++static void dwc3_core_soft_reset(struct dwc3 *dwc) ++{ ++ u32 reg; ++ ++ /* Before Resetting PHY, put Core in Reset */ ++ reg = dwc3_readl(dwc->regs, DWC3_GCTL); ++ reg |= DWC3_GCTL_CORESOFTRESET; ++ dwc3_writel(dwc->regs, DWC3_GCTL, reg); ++ ++ /* Assert USB3 PHY reset */ ++ reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); ++ reg |= DWC3_GUSB3PIPECTL_PHYSOFTRST; ++ dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); ++ ++ /* Assert USB2 PHY reset */ ++ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); ++ reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST; ++ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); ++ ++ mdelay(100); ++ ++ /* Clear USB3 PHY reset */ ++ reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); ++ reg &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST; ++ dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); ++ ++ /* Clear USB2 PHY reset */ ++ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); ++ reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST; ++ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); ++ ++ /* After PHYs are stable we can take Core out of reset state */ ++ reg = dwc3_readl(dwc->regs, DWC3_GCTL); ++ reg &= ~DWC3_GCTL_CORESOFTRESET; ++ dwc3_writel(dwc->regs, DWC3_GCTL, reg); ++} ++ ++/** ++ * dwc3_free_one_event_buffer - Frees one event buffer ++ * @dwc: Pointer to our controller context structure ++ * @evt: Pointer to event buffer to be freed ++ */ ++static void dwc3_free_one_event_buffer(struct dwc3 *dwc, ++ struct dwc3_event_buffer *evt) ++{ ++ dma_free_coherent(dwc->dev, evt->length, evt->buf, evt->dma); ++ kfree(evt); ++} ++ ++/** ++ * dwc3_alloc_one_event_buffer - Allocated one event buffer structure ++ * @dwc: Pointer to our controller context structure ++ * @length: size of the event buffer ++ * ++ * Returns a pointer to the allocated event buffer structure on succes ++ * otherwise ERR_PTR(errno). ++ */ ++static struct dwc3_event_buffer *__devinit ++dwc3_alloc_one_event_buffer(struct dwc3 *dwc, unsigned length) ++{ ++ struct dwc3_event_buffer *evt; ++ ++ evt = kzalloc(sizeof(*evt), GFP_KERNEL); ++ if (!evt) ++ return ERR_PTR(-ENOMEM); ++ ++ evt->dwc = dwc; ++ evt->length = length; ++ evt->buf = dma_alloc_coherent(dwc->dev, length, ++ &evt->dma, GFP_KERNEL); ++ if (!evt->buf) { ++ kfree(evt); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ return evt; ++} ++ ++/** ++ * dwc3_free_event_buffers - frees all allocated event buffers ++ * @dwc: Pointer to our controller context structure ++ */ ++static void dwc3_free_event_buffers(struct dwc3 *dwc) ++{ ++ struct dwc3_event_buffer *evt; ++ int i; ++ ++ for (i = 0; i < dwc->num_event_buffers; i++) { ++ evt = dwc->ev_buffs[i]; ++ if (evt) { ++ dwc3_free_one_event_buffer(dwc, evt); ++ dwc->ev_buffs[i] = NULL; ++ } ++ } ++} ++ ++/** ++ * dwc3_alloc_event_buffers - Allocates @num event buffers of size @length ++ * @dwc: Pointer to out controller context structure ++ * @length: size of event buffer ++ * ++ * Returns 0 on success otherwise negative errno. In error the case, dwc ++ * may contain some buffers allocated but not all which were requested. ++ */ ++static int __devinit dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length) ++{ ++ int num; ++ int i; ++ ++ num = DWC3_NUM_INT(dwc->hwparams.hwparams1); ++ dwc->num_event_buffers = num; ++ ++ dwc->ev_buffs = kzalloc(sizeof(*dwc->ev_buffs) * num, GFP_KERNEL); ++ if (!dwc->ev_buffs) { ++ dev_err(dwc->dev, "can't allocate event buffers array\n"); ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < num; i++) { ++ struct dwc3_event_buffer *evt; ++ ++ evt = dwc3_alloc_one_event_buffer(dwc, length); ++ if (IS_ERR(evt)) { ++ dev_err(dwc->dev, "can't allocate event buffer\n"); ++ return PTR_ERR(evt); ++ } ++ dwc->ev_buffs[i] = evt; ++ } ++ ++ return 0; ++} ++ ++/** ++ * dwc3_event_buffers_setup - setup our allocated event buffers ++ * @dwc: Pointer to out controller context structure ++ * ++ * Returns 0 on success otherwise negative errno. ++ */ ++static int __devinit dwc3_event_buffers_setup(struct dwc3 *dwc) ++{ ++ struct dwc3_event_buffer *evt; ++ int n; ++ ++ for (n = 0; n < dwc->num_event_buffers; n++) { ++ evt = dwc->ev_buffs[n]; ++ dev_dbg(dwc->dev, "Event buf %p dma %08llx length %d\n", ++ evt->buf, (unsigned long long) evt->dma, ++ evt->length); ++ ++ dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), ++ lower_32_bits(evt->dma)); ++ dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), ++ upper_32_bits(evt->dma)); ++ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), ++ evt->length & 0xffff); ++ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0); ++ } ++ ++ return 0; ++} ++ ++static void dwc3_event_buffers_cleanup(struct dwc3 *dwc) ++{ ++ struct dwc3_event_buffer *evt; ++ int n; ++ ++ for (n = 0; n < dwc->num_event_buffers; n++) { ++ evt = dwc->ev_buffs[n]; ++ dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), 0); ++ dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), 0); ++ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), 0); ++ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0); ++ } ++} ++ ++static void __devinit dwc3_cache_hwparams(struct dwc3 *dwc) ++{ ++ struct dwc3_hwparams *parms = &dwc->hwparams; ++ ++ parms->hwparams0 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS0); ++ parms->hwparams1 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS1); ++ parms->hwparams2 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS2); ++ parms->hwparams3 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS3); ++ parms->hwparams4 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS4); ++ parms->hwparams5 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS5); ++ parms->hwparams6 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6); ++ parms->hwparams7 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS7); ++ parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8); ++} ++ ++/** ++ * dwc3_core_init - Low-level initialization of DWC3 Core ++ * @dwc: Pointer to our controller context structure ++ * ++ * Returns 0 on success otherwise negative errno. ++ */ ++static int __devinit dwc3_core_init(struct dwc3 *dwc) ++{ ++ unsigned long timeout; ++ u32 reg; ++ int ret; ++ ++ reg = dwc3_readl(dwc->regs, DWC3_GSNPSID); ++ /* This should read as U3 followed by revision number */ ++ if ((reg & DWC3_GSNPSID_MASK) != 0x55330000) { ++ dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n"); ++ ret = -ENODEV; ++ goto err0; ++ } ++ dwc->revision = reg & DWC3_GSNPSREV_MASK; ++ ++ dwc3_core_soft_reset(dwc); ++ ++ /* issue device SoftReset too */ ++ timeout = 500; ++ dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST); ++ do { ++ reg = dwc3_readl(dwc->regs, DWC3_DCTL); ++ if (!(reg & DWC3_DCTL_CSFTRST)) ++ break; ++ ++ mdelay(1); ++ timeout--; ++ if (!timeout) { ++ dev_err(dwc->dev, "Reset Timed Out\n"); ++ ret = -ETIMEDOUT; ++ goto err0; ++ } ++ ++ cpu_relax(); ++ } while (true); ++ ++ dwc3_cache_hwparams(dwc); ++ ++ reg = dwc3_readl(dwc->regs, DWC3_GCTL); ++ reg &= ~DWC3_GCTL_SCALEDOWN(3); ++ reg &= ~DWC3_GCTL_DISSCRAMBLE; ++ ++ switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) { ++ case DWC3_GHWPARAMS1_EN_PWROPT_CLK: ++ reg &= ~DWC3_GCTL_DSBLCLKGTNG; ++ break; ++ default: ++ dev_dbg(dwc->dev, "No power optimization available\n"); ++ } ++ ++ /* ++ * WORKAROUND: DWC3 revisions <1.90a have a bug ++ * when The device fails to connect at SuperSpeed ++ * and falls back to high-speed mode which causes ++ * the device to enter in a Connect/Disconnect loop ++ */ ++ if (dwc->revision < DWC3_REVISION_190A) ++ reg |= DWC3_GCTL_U2RSTECN; ++ ++ dwc3_writel(dwc->regs, DWC3_GCTL, reg); ++ ++ ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE); ++ if (ret) { ++ dev_err(dwc->dev, "failed to allocate event buffers\n"); ++ ret = -ENOMEM; ++ goto err1; ++ } ++ ++ ret = dwc3_event_buffers_setup(dwc); ++ if (ret) { ++ dev_err(dwc->dev, "failed to setup event buffers\n"); ++ goto err1; ++ } ++ ++ return 0; ++ ++err1: ++ dwc3_free_event_buffers(dwc); ++ ++err0: ++ return ret; ++} ++ ++static void dwc3_core_exit(struct dwc3 *dwc) ++{ ++ dwc3_event_buffers_cleanup(dwc); ++ dwc3_free_event_buffers(dwc); ++} ++ ++#define DWC3_ALIGN_MASK (16 - 1) ++ ++struct dwc3 *global_dwc3; ++ ++int __devinit dwc3_probe(struct platform_device *pdev) ++{ ++ struct dwc3 *dwc; ++ ++ int ret = -ENOMEM; ++ ++ void __iomem *regs; ++ void *mem; ++ ++ u8 mode; ++ ++ mem = kzalloc(sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL); ++ if (!mem) { ++ dev_err(&pdev->dev, "not enough memory\n"); ++ goto err0; ++ } ++ dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1); ++ dwc->mem = mem; ++ global_dwc3 = dwc; ++ ++ dwc->regs = CONFIG_USB_DWC3_UDC_REGS; ++ dwc->regs_size = DWC3_USB_REGS_SIZE; ++ dwc->dev = &pdev->dev; ++ dwc->irq = 0; ++ ++ if (!strncmp("super", maximum_speed, 5)) ++ dwc->maximum_speed = DWC3_DCFG_SUPERSPEED; ++ else if (!strncmp("high", maximum_speed, 4)) ++ dwc->maximum_speed = DWC3_DCFG_HIGHSPEED; ++ else if (!strncmp("full", maximum_speed, 4)) ++ dwc->maximum_speed = DWC3_DCFG_FULLSPEED1; ++ else if (!strncmp("low", maximum_speed, 3)) ++ dwc->maximum_speed = DWC3_DCFG_LOWSPEED; ++ else ++ dwc->maximum_speed = DWC3_DCFG_SUPERSPEED; ++ ++ pm_runtime_enable(&pdev->dev); ++ pm_runtime_get_sync(&pdev->dev); ++ pm_runtime_forbid(&pdev->dev); ++ ++ ret = dwc3_core_init(dwc); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to initialize core\n"); ++ goto err3; ++ } ++ ++ mode = DWC3_MODE_DEVICE /*DWC3_MODE(dwc->hwparams.hwparams0)*/; ++ ++ switch (mode) { ++ case DWC3_MODE_DEVICE: ++ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); ++ ret = dwc3_gadget_init(dwc); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to initialize gadget\n"); ++ goto err4; ++ } ++ break; ++ case DWC3_MODE_HOST: ++ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST); ++ ret = dwc3_host_init(dwc); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to initialize host\n"); ++ goto err4; ++ } ++ break; ++ case DWC3_MODE_DRD: ++ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG); ++ ret = dwc3_host_init(dwc); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to initialize host\n"); ++ goto err4; ++ } ++ ++ ret = dwc3_gadget_init(dwc); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to initialize gadget\n"); ++ goto err4; ++ } ++ break; ++ default: ++ dev_err(&pdev->dev, "Unsupported mode of operation %d\n", mode); ++ goto err4; ++ } ++ dwc->mode = mode; ++ ++ pm_runtime_allow(&pdev->dev); ++ ++ return 0; ++err4: ++ dwc3_core_exit(dwc); ++ ++err3: ++ iounmap(regs); ++ kfree(dwc->mem); ++err0: ++ return ret; ++} ++ ++int __devexit dwc3_remove(struct platform_device *pdev) ++{ ++ struct dwc3 *dwc = global_dwc3; ++ ++ dwc3_debugfs_exit(dwc); ++ ++ switch (dwc->mode) { ++ case DWC3_MODE_DEVICE: ++ dwc3_gadget_exit(dwc); ++ break; ++ case DWC3_MODE_HOST: ++ dwc3_host_exit(dwc); ++ break; ++ case DWC3_MODE_DRD: ++ dwc3_host_exit(dwc); ++ dwc3_gadget_exit(dwc); ++ break; ++ default: ++ /* do nothing */ ++ break; ++ } ++ ++ dwc3_core_exit(dwc); ++ iounmap(dwc->regs); ++ kfree(dwc->mem); ++ ++ return 0; ++} ++ ++int usb_gadget_init_udc(void) ++{ ++ return dwc3_probe(NULL); ++} ++void usb_gadget_exit_udc(void) ++{ ++ dwc3_remove(NULL); ++} ++ ++irqreturn_t dwc3_interrupt(int irq, void *_dwc); ++int usb_gadget_handle_interrupts(void) ++{ ++ dwc3_interrupt(0, global_dwc3); ++ WATCHDOG_RESET(); ++ return 0; ++} +diff --git a/drivers/usb/dwc3/dwc3_ep0.c b/drivers/usb/dwc3/dwc3_ep0.c +new file mode 100644 +index 0000000..2e0d466 +--- /dev/null ++++ b/drivers/usb/dwc3/dwc3_ep0.c +@@ -0,0 +1,851 @@ ++/** ++ * ep0.c - DesignWare USB3 DRD Controller Endpoint 0 Handling ++ * ++ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com ++ * ++ * Authors: Felipe Balbi , ++ * Sebastian Andrzej Siewior ++ * ++ * 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 the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not 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. ++ * ++ * 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 MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. ++ */ ++ ++#include ++ ++#include ++#include ++/* #include */ ++ ++#include "core.h" ++#include "gadget.h" ++#include "io.h" ++ ++static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum); ++ ++static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state) ++{ ++ switch (state) { ++ case EP0_UNCONNECTED: ++ return "Unconnected"; ++ case EP0_SETUP_PHASE: ++ return "Setup Phase"; ++ case EP0_DATA_PHASE: ++ return "Data Phase"; ++ case EP0_STATUS_PHASE: ++ return "Status Phase"; ++ default: ++ return "UNKNOWN"; ++ } ++} ++ ++static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, ++ u32 len, u32 type) ++{ ++ struct dwc3_gadget_ep_cmd_params params; ++ struct dwc3_trb_hw *trb_hw; ++ struct dwc3_trb trb; ++ struct dwc3_ep *dep; ++ ++ int ret; ++ ++ dep = dwc->eps[epnum]; ++ if (dep->flags & DWC3_EP_BUSY) { ++ dev_vdbg(dwc->dev, "%s: still busy\n", dep->name); ++ return 0; ++ } ++ ++ trb_hw = dwc->ep0_trb; ++ memset(&trb, 0, sizeof(trb)); ++ ++ trb.trbctl = type; ++ trb.bplh = buf_dma; ++ trb.length = len; ++ ++ trb.hwo = 1; ++ trb.lst = 1; ++ trb.ioc = 1; ++ trb.isp_imi = 1; ++ ++ dwc3_trb_to_hw(&trb, trb_hw); ++ ++ memset(¶ms, 0, sizeof(params)); ++ params.param0 = upper_32_bits(dwc->ep0_trb_addr); ++ params.param1 = lower_32_bits(dwc->ep0_trb_addr); ++ ++ ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, ++ DWC3_DEPCMD_STARTTRANSFER, ¶ms); ++ if (ret < 0) { ++ dev_dbg(dwc->dev, "failed to send STARTTRANSFER command\n"); ++ return ret; ++ } ++ ++ dep->flags |= DWC3_EP_BUSY; ++ dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc, ++ dep->number); ++ ++ dwc->ep0_next_event = DWC3_EP0_COMPLETE; ++ ++ return 0; ++} ++ ++static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, ++ struct dwc3_request *req) ++{ ++ struct dwc3 *dwc = dep->dwc; ++ u32 type; ++ int ret = 0; ++ ++ req->request.actual = 0; ++ req->request.status = -EINPROGRESS; ++ req->epnum = dep->number; ++ ++ list_add_tail(&req->list, &dep->request_list); ++ ++ /* ++ * Gadget driver might not be quick enough to queue a request ++ * before we get a Transfer Not Ready event on this endpoint. ++ * ++ * In that case, we will set DWC3_EP_PENDING_REQUEST. When that ++ * flag is set, it's telling us that as soon as Gadget queues the ++ * required request, we should kick the transfer here because the ++ * IRQ we were waiting for is long gone. ++ */ ++ if (dep->flags & DWC3_EP_PENDING_REQUEST) { ++ unsigned direction; ++ ++ direction = !!(dep->flags & DWC3_EP0_DIR_IN); ++ ++ if (dwc->ep0state == EP0_STATUS_PHASE) { ++ type = dwc->three_stage_setup ++ ? DWC3_TRBCTL_CONTROL_STATUS3 ++ : DWC3_TRBCTL_CONTROL_STATUS2; ++ } else if (dwc->ep0state == EP0_DATA_PHASE) { ++ type = DWC3_TRBCTL_CONTROL_DATA; ++ } else { ++ /* should never happen */ ++ WARN_ON(1); ++ return 0; ++ } ++ ++ ret = dwc3_ep0_start_trans(dwc, direction, ++ req->request.dma, req->request.length, type); ++ dep->flags &= ~(DWC3_EP_PENDING_REQUEST | ++ DWC3_EP0_DIR_IN); ++ ++ } else if (dwc->delayed_status && (dwc->ep0state == EP0_STATUS_PHASE)) { ++ dwc->delayed_status = false; ++ dwc3_ep0_do_control_status(dwc, 1); ++ } ++ ++ return ret; ++} ++ ++int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, ++ gfp_t gfp_flags) ++{ ++ struct dwc3_request *req = to_dwc3_request(request); ++ struct dwc3_ep *dep = to_dwc3_ep(ep); ++ struct dwc3 *dwc = dep->dwc; ++ ++ unsigned long flags; ++ ++ int ret; ++ ++ spin_lock_irqsave(&dwc->lock, flags); ++ if (!dep->desc) { ++ dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n", ++ request, dep->name); ++ ret = -ESHUTDOWN; ++ goto out; ++ } ++ ++ /* we share one TRB for ep0/1 */ ++ if (!list_empty(&dep->request_list)) { ++ ret = -EBUSY; ++ goto out; ++ } ++ ++ dev_vdbg(dwc->dev, "queueing request %p to %s length %d, state '%s'\n", ++ request, dep->name, request->length, ++ dwc3_ep0_state_string(dwc->ep0state)); ++ ++ ret = __dwc3_gadget_ep0_queue(dep, req); ++ ++out: ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ ++ return ret; ++} ++ ++static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) ++{ ++ struct dwc3_ep *dep = dwc->eps[0]; ++ ++ /* stall is always issued on EP0 */ ++ __dwc3_gadget_ep_set_halt(dep, 1); ++ dep->flags = DWC3_EP_ENABLED; ++ dwc->delayed_status = false; ++ ++ if (!list_empty(&dep->request_list)) { ++ struct dwc3_request *req; ++ ++ req = next_request(&dep->request_list); ++ dwc3_gadget_giveback(dep, req, -ECONNRESET); ++ } ++ ++ dwc->ep0state = EP0_SETUP_PHASE; ++ dwc3_ep0_out_start(dwc); ++} ++ ++void dwc3_ep0_out_start(struct dwc3 *dwc) ++{ ++ int ret; ++ ++ ret = dwc3_ep0_start_trans(dwc, 0, dwc->ctrl_req_addr, 8, ++ DWC3_TRBCTL_CONTROL_SETUP); ++ WARN_ON(ret < 0); ++} ++ ++static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le) ++{ ++ struct dwc3_ep *dep; ++ u32 windex = le16_to_cpu(wIndex_le); ++ u32 epnum; ++ ++ epnum = (windex & USB_ENDPOINT_NUMBER_MASK) << 1; ++ if ((windex & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) ++ epnum |= 1; ++ ++ dep = dwc->eps[epnum]; ++ if (dep->flags & DWC3_EP_ENABLED) ++ return dep; ++ ++ return NULL; ++} ++ ++static void dwc3_ep0_status_cmpl(struct usb_ep *ep, struct usb_request *req) ++{ ++} ++/* ++ * ch 9.4.5 ++ */ ++static int dwc3_ep0_handle_status(struct dwc3 *dwc, ++ struct usb_ctrlrequest *ctrl) ++{ ++ struct dwc3_ep *dep; ++ u32 recip; ++ u16 usb_status = 0; ++ __le16 *response_pkt; ++ ++ recip = ctrl->bRequestType & USB_RECIP_MASK; ++ switch (recip) { ++ case USB_RECIP_DEVICE: ++ /* ++ * We are self-powered. U1/U2/LTM will be set later ++ * once we handle this states. RemoteWakeup is 0 on SS ++ */ ++ usb_status |= dwc->is_selfpowered << USB_DEVICE_SELF_POWERED; ++ break; ++ ++ case USB_RECIP_INTERFACE: ++ /* ++ * Function Remote Wake Capable D0 ++ * Function Remote Wakeup D1 ++ */ ++ break; ++ ++ case USB_RECIP_ENDPOINT: ++ dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex); ++ if (!dep) ++ return -EINVAL; ++ ++ if (dep->flags & DWC3_EP_STALL) ++ usb_status = 1 << USB_ENDPOINT_HALT; ++ break; ++ default: ++ return -EINVAL; ++ }; ++ ++ response_pkt = (__le16 *) dwc->setup_buf; ++ *response_pkt = cpu_to_le16(usb_status); ++ ++ dep = dwc->eps[0]; ++ dwc->ep0_usb_req.dep = dep; ++ dwc->ep0_usb_req.request.length = sizeof(*response_pkt); ++ dwc->ep0_usb_req.request.dma = dwc->setup_buf_addr; ++ dwc->ep0_usb_req.request.complete = dwc3_ep0_status_cmpl; ++ ++ return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req); ++} ++ ++static int dwc3_ep0_handle_feature(struct dwc3 *dwc, ++ struct usb_ctrlrequest *ctrl, int set) ++{ ++ struct dwc3_ep *dep; ++ u32 recip; ++ u32 wValue; ++ u32 wIndex; ++ u32 reg; ++ int ret; ++ u32 mode; ++ ++ wValue = le16_to_cpu(ctrl->wValue); ++ wIndex = le16_to_cpu(ctrl->wIndex); ++ recip = ctrl->bRequestType & USB_RECIP_MASK; ++ switch (recip) { ++ case USB_RECIP_DEVICE: ++ ++ /* ++ * 9.4.1 says only only for SS, in AddressState only for ++ * default control pipe ++ */ ++ switch (wValue) { ++ case USB_DEVICE_U1_ENABLE: ++ case USB_DEVICE_U2_ENABLE: ++ case USB_DEVICE_LTM_ENABLE: ++ if (dwc->dev_state != DWC3_CONFIGURED_STATE) ++ return -EINVAL; ++ if (dwc->speed != DWC3_DSTS_SUPERSPEED) ++ return -EINVAL; ++ } ++ ++ /* XXX add U[12] & LTM */ ++ switch (wValue) { ++ case USB_DEVICE_REMOTE_WAKEUP: ++ break; ++ case USB_DEVICE_U1_ENABLE: ++ break; ++ case USB_DEVICE_U2_ENABLE: ++ break; ++ case USB_DEVICE_LTM_ENABLE: ++ break; ++ ++ case USB_DEVICE_TEST_MODE: ++ if ((wIndex & 0xff) != 0) ++ return -EINVAL; ++ if (!set) ++ return -EINVAL; ++ ++ mode = wIndex >> 8; ++ reg = dwc3_readl(dwc->regs, DWC3_DCTL); ++ reg &= ~DWC3_DCTL_TSTCTRL_MASK; ++ ++ switch (mode) { ++ case TEST_J: ++ case TEST_K: ++ case TEST_SE0_NAK: ++ case TEST_PACKET: ++ case TEST_FORCE_EN: ++ reg |= mode << 1; ++ break; ++ default: ++ return -EINVAL; ++ } ++ dwc3_writel(dwc->regs, DWC3_DCTL, reg); ++ break; ++ default: ++ return -EINVAL; ++ } ++ break; ++ ++ case USB_RECIP_INTERFACE: ++ switch (wValue) { ++ case USB_INTRF_FUNC_SUSPEND: ++ if (wIndex & USB_INTRF_FUNC_SUSPEND_LP) ++ /* XXX enable Low power suspend */ ++ ; ++ if (wIndex & USB_INTRF_FUNC_SUSPEND_RW) ++ /* XXX enable remote wakeup */ ++ ; ++ break; ++ default: ++ return -EINVAL; ++ } ++ break; ++ ++ case USB_RECIP_ENDPOINT: ++ switch (wValue) { ++ case USB_ENDPOINT_HALT: ++ dep = dwc3_wIndex_to_dep(dwc, wIndex); ++ if (!dep) ++ return -EINVAL; ++ ret = __dwc3_gadget_ep_set_halt(dep, set); ++ if (ret) ++ return -EINVAL; ++ break; ++ default: ++ return -EINVAL; ++ } ++ break; ++ ++ default: ++ return -EINVAL; ++ }; ++ ++ return 0; ++} ++ ++static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) ++{ ++ u32 addr; ++ u32 reg; ++ ++ addr = le16_to_cpu(ctrl->wValue); ++ if (addr > 127) { ++ dev_dbg(dwc->dev, "invalid device address %d\n", addr); ++ return -EINVAL; ++ } ++ ++ if (dwc->dev_state == DWC3_CONFIGURED_STATE) { ++ dev_dbg(dwc->dev, "trying to set address when configured\n"); ++ return -EINVAL; ++ } ++ ++ reg = dwc3_readl(dwc->regs, DWC3_DCFG); ++ reg &= ~(DWC3_DCFG_DEVADDR_MASK); ++ reg |= DWC3_DCFG_DEVADDR(addr); ++ dwc3_writel(dwc->regs, DWC3_DCFG, reg); ++ ++ if (addr) ++ dwc->dev_state = DWC3_ADDRESS_STATE; ++ else ++ dwc->dev_state = DWC3_DEFAULT_STATE; ++ ++ return 0; ++} ++ ++static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) ++{ ++ int ret; ++ ++ spin_unlock(&dwc->lock); ++ ret = dwc->gadget_driver->setup(&dwc->gadget, ctrl); ++ spin_lock(&dwc->lock); ++ return ret; ++} ++ ++static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) ++{ ++ u32 cfg; ++ int ret; ++ ++ dwc->start_config_issued = false; ++ cfg = le16_to_cpu(ctrl->wValue); ++ ++ switch (dwc->dev_state) { ++ case DWC3_DEFAULT_STATE: ++ return -EINVAL; ++ break; ++ ++ case DWC3_ADDRESS_STATE: ++ ret = dwc3_ep0_delegate_req(dwc, ctrl); ++ /* if the cfg matches and the cfg is non zero */ ++ if (!ret && cfg) ++ dwc->dev_state = DWC3_CONFIGURED_STATE; ++ break; ++ ++ case DWC3_CONFIGURED_STATE: ++ ret = dwc3_ep0_delegate_req(dwc, ctrl); ++ if (!cfg) ++ dwc->dev_state = DWC3_ADDRESS_STATE; ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ return ret; ++} ++ ++static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) ++{ ++ int ret; ++ ++ switch (ctrl->bRequest) { ++ case USB_REQ_GET_STATUS: ++ dev_vdbg(dwc->dev, "USB_REQ_GET_STATUS\n"); ++ ret = dwc3_ep0_handle_status(dwc, ctrl); ++ break; ++ case USB_REQ_CLEAR_FEATURE: ++ dev_vdbg(dwc->dev, "USB_REQ_CLEAR_FEATURE\n"); ++ ret = dwc3_ep0_handle_feature(dwc, ctrl, 0); ++ break; ++ case USB_REQ_SET_FEATURE: ++ dev_vdbg(dwc->dev, "USB_REQ_SET_FEATURE\n"); ++ ret = dwc3_ep0_handle_feature(dwc, ctrl, 1); ++ break; ++ case USB_REQ_SET_ADDRESS: ++ dev_vdbg(dwc->dev, "USB_REQ_SET_ADDRESS\n"); ++ ret = dwc3_ep0_set_address(dwc, ctrl); ++ break; ++ case USB_REQ_SET_CONFIGURATION: ++ dev_vdbg(dwc->dev, "USB_REQ_SET_CONFIGURATION\n"); ++ ret = dwc3_ep0_set_config(dwc, ctrl); ++ break; ++ default: ++ dev_vdbg(dwc->dev, "Forwarding to gadget driver\n"); ++ ret = dwc3_ep0_delegate_req(dwc, ctrl); ++ break; ++ }; ++ ++ return ret; ++} ++ ++static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, ++ const struct dwc3_event_depevt *event) ++{ ++ struct usb_ctrlrequest *ctrl = dwc->ctrl_req; ++ int ret; ++ u32 len; ++ ++ if (!dwc->gadget_driver) ++ goto err; ++ ++ len = le16_to_cpu(ctrl->wLength); ++ if (!len) { ++ dwc->three_stage_setup = false; ++ dwc->ep0_expect_in = false; ++ dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS; ++ } else { ++ dwc->three_stage_setup = true; ++ dwc->ep0_expect_in = !!(ctrl->bRequestType & USB_DIR_IN); ++ dwc->ep0_next_event = DWC3_EP0_NRDY_DATA; ++ } ++ ++ if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) ++ ret = dwc3_ep0_std_request(dwc, ctrl); ++ else ++ ret = dwc3_ep0_delegate_req(dwc, ctrl); ++ ++ if (ret == USB_GADGET_DELAYED_STATUS) ++ dwc->delayed_status = true; ++ ++ if (ret >= 0) ++ return; ++ ++err: ++ dwc3_ep0_stall_and_restart(dwc); ++} ++ ++static void dwc3_ep0_complete_data(struct dwc3 *dwc, ++ const struct dwc3_event_depevt *event) ++{ ++ struct dwc3_request *r = NULL; ++ struct usb_request *ur; ++ struct dwc3_trb trb; ++ struct dwc3_ep *ep0; ++ u32 transferred; ++ u8 epnum; ++ ++ epnum = event->endpoint_number; ++ ep0 = dwc->eps[0]; ++ ++ dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS; ++ ++ r = next_request(&ep0->request_list); ++ ur = &r->request; ++ ++ dwc3_trb_to_nat(dwc->ep0_trb, &trb); ++ ++ if (dwc->ep0_bounced) { ++ ++ transferred = min_t(u32, ur->length, ++ ep0->endpoint.maxpacket - trb.length); ++ memcpy(ur->buf, dwc->ep0_bounce, transferred); ++ dwc->ep0_bounced = false; ++ } else { ++ transferred = ur->length - trb.length; ++ ur->actual += transferred; ++ } ++ ++ if ((epnum & 1) && ur->actual < ur->length) { ++ /* for some reason we did not get everything out */ ++ ++ dwc3_ep0_stall_and_restart(dwc); ++ } else { ++ /* ++ * handle the case where we have to send a zero packet. This ++ * seems to be case when req.length > maxpacket. Could it be? ++ */ ++ if (r) ++ dwc3_gadget_giveback(ep0, r, 0); ++ } ++} ++ ++static void dwc3_ep0_complete_req(struct dwc3 *dwc, ++ const struct dwc3_event_depevt *event) ++{ ++ struct dwc3_request *r; ++ struct dwc3_ep *dep; ++ ++ dep = dwc->eps[0]; ++ ++ if (!list_empty(&dep->request_list)) { ++ r = next_request(&dep->request_list); ++ ++ dwc3_gadget_giveback(dep, r, 0); ++ } ++ ++ dwc->ep0state = EP0_SETUP_PHASE; ++ dwc3_ep0_out_start(dwc); ++} ++ ++static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, ++ const struct dwc3_event_depevt *event) ++{ ++ struct dwc3_ep *dep = dwc->eps[event->endpoint_number]; ++ ++ dep->flags &= ~DWC3_EP_BUSY; ++ dwc->setup_packet_pending = false; ++ ++ switch (dwc->ep0state) { ++ case EP0_SETUP_PHASE: ++ dev_vdbg(dwc->dev, "Inspecting Setup Bytes\n"); ++ dwc3_ep0_inspect_setup(dwc, event); ++ break; ++ ++ case EP0_DATA_PHASE: ++ dev_vdbg(dwc->dev, "Data Phase\n"); ++ dwc3_ep0_complete_data(dwc, event); ++ break; ++ ++ case EP0_STATUS_PHASE: ++ dev_vdbg(dwc->dev, "Status Phase\n"); ++ dwc3_ep0_complete_req(dwc, event); ++ break; ++ default: ++ WARN(true, "UNKNOWN ep0state %d\n", dwc->ep0state); ++ } ++} ++ ++static void dwc3_ep0_do_control_setup(struct dwc3 *dwc, ++ const struct dwc3_event_depevt *event) ++{ ++ dwc3_ep0_out_start(dwc); ++} ++ ++static void dwc3_ep0_do_control_data(struct dwc3 *dwc, ++ const struct dwc3_event_depevt *event) ++{ ++ struct dwc3_ep *dep; ++ struct dwc3_request *req; ++ int ret; ++ ++ dep = dwc->eps[0]; ++ ++ if (list_empty(&dep->request_list)) { ++ dev_vdbg(dwc->dev, "pending request for EP0 Data phase\n"); ++ dep->flags |= DWC3_EP_PENDING_REQUEST; ++ ++ if (event->endpoint_number) ++ dep->flags |= DWC3_EP0_DIR_IN; ++ return; ++ } ++ ++ req = next_request(&dep->request_list); ++ req->direction = !!event->endpoint_number; ++ ++ if (req->request.length == 0) { ++ ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, ++ dwc->ctrl_req_addr, 0, ++ DWC3_TRBCTL_CONTROL_DATA); ++ } else if ((req->request.length % dep->endpoint.maxpacket) ++ && (event->endpoint_number == 0)) { ++ dwc3_map_buffer_to_dma(req); ++ ++ WARN_ON(req->request.length > dep->endpoint.maxpacket); ++ ++ dwc->ep0_bounced = true; ++ ++ /* ++ * REVISIT in case request length is bigger than EP0 ++ * wMaxPacketSize, we will need two chained TRBs to handle ++ * the transfer. ++ */ ++ ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, ++ dwc->ep0_bounce_addr, dep->endpoint.maxpacket, ++ DWC3_TRBCTL_CONTROL_DATA); ++ } else { ++ dwc3_map_buffer_to_dma(req); ++ ++ ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, ++ req->request.dma, req->request.length, ++ DWC3_TRBCTL_CONTROL_DATA); ++ } ++ ++ WARN_ON(ret < 0); ++} ++ ++static int dwc3_ep0_start_control_status(struct dwc3_ep *dep) ++{ ++ struct dwc3 *dwc = dep->dwc; ++ u32 type; ++ ++ type = dwc->three_stage_setup ? DWC3_TRBCTL_CONTROL_STATUS3 ++ : DWC3_TRBCTL_CONTROL_STATUS2; ++ ++ return dwc3_ep0_start_trans(dwc, dep->number, ++ dwc->ctrl_req_addr, 0, type); ++} ++ ++static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum) ++{ ++ struct dwc3_ep *dep = dwc->eps[epnum]; ++ ++ WARN_ON(dwc3_ep0_start_control_status(dep)); ++} ++ ++static void dwc3_ep0_xfernotready(struct dwc3 *dwc, ++ const struct dwc3_event_depevt *event) ++{ ++ dwc->setup_packet_pending = true; ++ ++ /* ++ * This part is very tricky: If we has just handled ++ * XferNotReady(Setup) and we're now expecting a ++ * XferComplete but, instead, we receive another ++ * XferNotReady(Setup), we should STALL and restart ++ * the state machine. ++ * ++ * In all other cases, we just continue waiting ++ * for the XferComplete event. ++ * ++ * We are a little bit unsafe here because we're ++ * not trying to ensure that last event was, indeed, ++ * XferNotReady(Setup). ++ * ++ * Still, we don't expect any condition where that ++ * should happen and, even if it does, it would be ++ * another error condition. ++ */ ++ if (dwc->ep0_next_event == DWC3_EP0_COMPLETE) { ++ switch (event->status) { ++ case DEPEVT_STATUS_CONTROL_SETUP: ++ dev_vdbg(dwc->dev, "Unexpected XferNotReady(Setup)\n"); ++ dwc3_ep0_stall_and_restart(dwc); ++ break; ++ case DEPEVT_STATUS_CONTROL_DATA: ++ /* FALLTHROUGH */ ++ case DEPEVT_STATUS_CONTROL_STATUS: ++ /* FALLTHROUGH */ ++ default: ++ dev_vdbg(dwc->dev, "waiting for XferComplete\n"); ++ } ++ ++ return; ++ } ++ ++ switch (event->status) { ++ case DEPEVT_STATUS_CONTROL_SETUP: ++ dev_vdbg(dwc->dev, "Control Setup\n"); ++ ++ dwc->ep0state = EP0_SETUP_PHASE; ++ ++ dwc3_ep0_do_control_setup(dwc, event); ++ break; ++ ++ case DEPEVT_STATUS_CONTROL_DATA: ++ dev_vdbg(dwc->dev, "Control Data\n"); ++ ++ dwc->ep0state = EP0_DATA_PHASE; ++ ++ if (dwc->ep0_next_event != DWC3_EP0_NRDY_DATA) { ++ dev_vdbg(dwc->dev, "Expected %d got %d\n", ++ dwc->ep0_next_event, ++ DWC3_EP0_NRDY_DATA); ++ ++ dwc3_ep0_stall_and_restart(dwc); ++ return; ++ } ++ ++ /* ++ * One of the possible error cases is when Host _does_ ++ * request for Data Phase, but it does so on the wrong ++ * direction. ++ * ++ * Here, we already know ep0_next_event is DATA (see above), ++ * so we only need to check for direction. ++ */ ++ if (dwc->ep0_expect_in != event->endpoint_number) { ++ dev_vdbg(dwc->dev, "Wrong direction for Data phase\n"); ++ dwc3_ep0_stall_and_restart(dwc); ++ return; ++ } ++ ++ dwc3_ep0_do_control_data(dwc, event); ++ break; ++ ++ case DEPEVT_STATUS_CONTROL_STATUS: ++ dev_vdbg(dwc->dev, "Control Status\n"); ++ ++ dwc->ep0state = EP0_STATUS_PHASE; ++ ++ if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS) { ++ dev_vdbg(dwc->dev, "Expected %d got %d\n", ++ dwc->ep0_next_event, ++ DWC3_EP0_NRDY_STATUS); ++ ++ dwc3_ep0_stall_and_restart(dwc); ++ return; ++ } ++ ++ if (dwc->delayed_status) { ++ WARN_ON_ONCE(event->endpoint_number != 1); ++ dev_vdbg(dwc->dev, "Mass Storage delayed status\n"); ++ return; ++ } ++ ++ dwc3_ep0_do_control_status(dwc, event->endpoint_number); ++ } ++} ++ ++void dwc3_ep0_interrupt(struct dwc3 *dwc, ++ const struct dwc3_event_depevt *event) ++{ ++ u8 epnum = event->endpoint_number; ++ ++ dev_dbg(dwc->dev, "%s while ep%d%s in state '%s'\n", ++ dwc3_ep_event_string(event->endpoint_event), ++ epnum >> 1, (epnum & 1) ? "in" : "out", ++ dwc3_ep0_state_string(dwc->ep0state)); ++ ++ switch (event->endpoint_event) { ++ case DWC3_DEPEVT_XFERCOMPLETE: ++ dwc3_ep0_xfer_complete(dwc, event); ++ break; ++ ++ case DWC3_DEPEVT_XFERNOTREADY: ++ dwc3_ep0_xfernotready(dwc, event); ++ break; ++ ++ case DWC3_DEPEVT_XFERINPROGRESS: ++ case DWC3_DEPEVT_RXTXFIFOEVT: ++ case DWC3_DEPEVT_STREAMEVT: ++ case DWC3_DEPEVT_EPCMDCMPLT: ++ break; ++ } ++} +diff --git a/drivers/usb/dwc3/dwc3_gadget.c b/drivers/usb/dwc3/dwc3_gadget.c +new file mode 100644 +index 0000000..7c1a479 +--- /dev/null ++++ b/drivers/usb/dwc3/dwc3_gadget.c +@@ -0,0 +1,2184 @@ ++/** ++ * gadget.c - DesignWare USB3 DRD Controller Gadget Framework Link ++ * ++ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com ++ * ++ * Authors: Felipe Balbi , ++ * Sebastian Andrzej Siewior ++ * ++ * 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 the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not 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. ++ * ++ * 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 MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. ++ */ ++ ++#include ++ ++#include ++#include ++ ++#include "core.h" ++#include "gadget.h" ++#include "io.h" ++ ++#define DMA_ADDR_INVALID (~(dma_addr_t)0) ++ ++void dwc3_map_buffer_to_dma(struct dwc3_request *req) ++{ ++ struct dwc3 *dwc = req->dep->dwc; ++ ++ if (req->request.length == 0) { ++ /* req->request.dma = dwc->setup_buf_addr; */ ++ return; ++ } ++ ++ if (req->request.dma == DMA_ADDR_INVALID) { ++ req->request.dma = dma_map_single(dwc->dev, req->request.buf, ++ req->request.length, req->direction ++ ? DMA_TO_DEVICE : DMA_FROM_DEVICE); ++ req->mapped = true; ++ } ++} ++ ++void dwc3_unmap_buffer_from_dma(struct dwc3_request *req) ++{ ++ struct dwc3 *dwc = req->dep->dwc; ++ ++ if (req->request.length == 0) { ++ req->request.dma = DMA_ADDR_INVALID; ++ return; ++ } ++ ++ if (req->mapped) { ++ dma_unmap_single(dwc->dev, req->request.dma, ++ req->request.length, req->direction ++ ? DMA_TO_DEVICE : DMA_FROM_DEVICE); ++ req->mapped = 0; ++ req->request.dma = DMA_ADDR_INVALID; ++ } ++} ++ ++void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, ++ int status) ++{ ++ struct dwc3 *dwc = dep->dwc; ++ ++ if (req->queued) { ++ dep->busy_slot++; ++ /* ++ * Skip LINK TRB. We can't use req->trb and check for ++ * DWC3_TRBCTL_LINK_TRB because it points the TRB we just ++ * completed (not the LINK TRB). ++ */ ++ if (((dep->busy_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) && ++ usb_endpoint_xfer_isoc(dep->desc)) ++ dep->busy_slot++; ++ } ++ list_del(&req->list); ++ ++ if (req->request.status == -EINPROGRESS) ++ req->request.status = status; ++ ++ dwc3_unmap_buffer_from_dma(req); ++ ++ dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n", ++ req, dep->name, req->request.actual, ++ req->request.length, status); ++ ++ spin_unlock(&dwc->lock); ++ req->request.complete(&req->dep->endpoint, &req->request); ++ spin_lock(&dwc->lock); ++} ++ ++static const char *dwc3_gadget_ep_cmd_string(u8 cmd) ++{ ++ switch (cmd) { ++ case DWC3_DEPCMD_DEPSTARTCFG: ++ return "Start New Configuration"; ++ case DWC3_DEPCMD_ENDTRANSFER: ++ return "End Transfer"; ++ case DWC3_DEPCMD_UPDATETRANSFER: ++ return "Update Transfer"; ++ case DWC3_DEPCMD_STARTTRANSFER: ++ return "Start Transfer"; ++ case DWC3_DEPCMD_CLEARSTALL: ++ return "Clear Stall"; ++ case DWC3_DEPCMD_SETSTALL: ++ return "Set Stall"; ++ case DWC3_DEPCMD_GETSEQNUMBER: ++ return "Get Data Sequence Number"; ++ case DWC3_DEPCMD_SETTRANSFRESOURCE: ++ return "Set Endpoint Transfer Resource"; ++ case DWC3_DEPCMD_SETEPCONFIG: ++ return "Set Endpoint Configuration"; ++ default: ++ return "UNKNOWN command"; ++ } ++} ++ ++int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, ++ unsigned cmd, struct dwc3_gadget_ep_cmd_params *params) ++{ ++ struct dwc3_ep *dep = dwc->eps[ep]; ++ u32 timeout = 500; ++ u32 reg; ++ ++ dev_vdbg(dwc->dev, "%s: cmd '%s' params %08x %08x %08x\n", ++ dep->name, ++ dwc3_gadget_ep_cmd_string(cmd), params->param0, ++ params->param1, params->param2); ++ ++ dwc3_writel(dwc->regs, DWC3_DEPCMDPAR0(ep), params->param0); ++ dwc3_writel(dwc->regs, DWC3_DEPCMDPAR1(ep), params->param1); ++ dwc3_writel(dwc->regs, DWC3_DEPCMDPAR2(ep), params->param2); ++ ++ dwc3_writel(dwc->regs, DWC3_DEPCMD(ep), cmd | DWC3_DEPCMD_CMDACT); ++ do { ++ reg = dwc3_readl(dwc->regs, DWC3_DEPCMD(ep)); ++ if (!(reg & DWC3_DEPCMD_CMDACT)) { ++ dev_vdbg(dwc->dev, "Command Complete --> %d\n", ++ DWC3_DEPCMD_STATUS(reg)); ++ return 0; ++ } ++ ++ /* ++ * We can't sleep here, because it is also called from ++ * interrupt context. ++ */ ++ timeout--; ++ if (!timeout) ++ return -ETIMEDOUT; ++ ++ udelay(1); ++ } while (1); ++} ++ ++static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep, ++ struct dwc3_trb_hw *trb) ++{ ++ u32 offset = (char *) trb - (char *) dep->trb_pool; ++ ++ return dep->trb_pool_dma + offset; ++} ++ ++static int dwc3_alloc_trb_pool(struct dwc3_ep *dep) ++{ ++ struct dwc3 *dwc = dep->dwc; ++ ++ if (dep->trb_pool) ++ return 0; ++ ++ if (dep->number == 0 || dep->number == 1) ++ return 0; ++ ++ dep->trb_pool = dma_alloc_coherent(dwc->dev, ++ sizeof(struct dwc3_trb) * DWC3_TRB_NUM, ++ &dep->trb_pool_dma, GFP_KERNEL); ++ if (!dep->trb_pool) { ++ dev_err(dep->dwc->dev, "failed to allocate trb pool for %s\n", ++ dep->name); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static void dwc3_free_trb_pool(struct dwc3_ep *dep) ++{ ++ struct dwc3 *dwc = dep->dwc; ++ ++ dma_free_coherent(dwc->dev, sizeof(struct dwc3_trb) * DWC3_TRB_NUM, ++ dep->trb_pool, dep->trb_pool_dma); ++ ++ dep->trb_pool = NULL; ++ dep->trb_pool_dma = 0; ++} ++ ++static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep) ++{ ++ struct dwc3_gadget_ep_cmd_params params; ++ u32 cmd; ++ ++ memset(¶ms, 0x00, sizeof(params)); ++ ++ if (dep->number != 1) { ++ cmd = DWC3_DEPCMD_DEPSTARTCFG; ++ /* XferRscIdx == 0 for ep0 and 2 for the remaining */ ++ if (dep->number > 1) { ++ if (dwc->start_config_issued) ++ return 0; ++ dwc->start_config_issued = true; ++ cmd |= DWC3_DEPCMD_PARAM(2); ++ } ++ ++ return dwc3_send_gadget_ep_cmd(dwc, 0, cmd, ¶ms); ++ } ++ ++ return 0; ++} ++ ++static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, ++ const struct usb_endpoint_descriptor *desc, ++ const struct usb_ss_ep_comp_descriptor *comp_desc) ++{ ++ struct dwc3_gadget_ep_cmd_params params; ++ ++ memset(¶ms, 0x00, sizeof(params)); ++ ++ params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc)) ++ | DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc)) ++ | DWC3_DEPCFG_BURST_SIZE(dep->endpoint.maxburst); ++ ++ params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN ++ | DWC3_DEPCFG_XFER_NOT_READY_EN; ++ ++#if 0 ++ if (comp_desc && USB_SS_MAX_STREAMS(comp_desc->bmAttributes) ++ && usb_endpoint_xfer_bulk(desc)) { ++ params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE ++ | DWC3_DEPCFG_STREAM_EVENT_EN; ++ dep->stream_capable = true; ++ } ++#endif ++ ++ if (usb_endpoint_xfer_isoc(desc)) ++ params.param1 |= DWC3_DEPCFG_XFER_IN_PROGRESS_EN; ++ ++ /* ++ * We are doing 1:1 mapping for endpoints, meaning ++ * Physical Endpoints 2 maps to Logical Endpoint 2 and ++ * so on. We consider the direction bit as part of the physical ++ * endpoint number. So USB endpoint 0x81 is 0x03. ++ */ ++ params.param1 |= DWC3_DEPCFG_EP_NUMBER(dep->number); ++ ++ /* ++ * We must use the lower 16 TX FIFOs even though ++ * HW might have more ++ */ ++ if (dep->direction) ++ params.param0 |= DWC3_DEPCFG_FIFO_NUMBER(dep->number >> 1); ++ ++ if (desc->bInterval) { ++ params.param1 |= DWC3_DEPCFG_BINTERVAL_M1(desc->bInterval - 1); ++ dep->interval = 1 << (desc->bInterval - 1); ++ } ++ ++ return dwc3_send_gadget_ep_cmd(dwc, dep->number, ++ DWC3_DEPCMD_SETEPCONFIG, ¶ms); ++} ++ ++static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep) ++{ ++ struct dwc3_gadget_ep_cmd_params params; ++ ++ memset(¶ms, 0x00, sizeof(params)); ++ ++ params.param0 = DWC3_DEPXFERCFG_NUM_XFER_RES(1); ++ ++ return dwc3_send_gadget_ep_cmd(dwc, dep->number, ++ DWC3_DEPCMD_SETTRANSFRESOURCE, ¶ms); ++} ++ ++/** ++ * __dwc3_gadget_ep_enable - Initializes a HW endpoint ++ * @dep: endpoint to be initialized ++ * @desc: USB Endpoint Descriptor ++ * ++ * Caller should take care of locking ++ */ ++static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, ++ const struct usb_endpoint_descriptor *desc, ++ const struct usb_ss_ep_comp_descriptor *comp_desc) ++{ ++ struct dwc3 *dwc = dep->dwc; ++ u32 reg; ++ int ret = -ENOMEM; ++ ++ if (!(dep->flags & DWC3_EP_ENABLED)) { ++ ret = dwc3_gadget_start_config(dwc, dep); ++ if (ret) ++ return ret; ++ } ++ ++ ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc); ++ if (ret) ++ return ret; ++ ++ if (!(dep->flags & DWC3_EP_ENABLED)) { ++ struct dwc3_trb_hw *trb_st_hw; ++ struct dwc3_trb_hw *trb_link_hw; ++ struct dwc3_trb trb_link; ++ ++ ret = dwc3_gadget_set_xfer_resource(dwc, dep); ++ if (ret) ++ return ret; ++ ++ dep->desc = desc; ++ dep->comp_desc = comp_desc; ++ dep->type = usb_endpoint_type(desc); ++ dep->flags |= DWC3_EP_ENABLED; ++ ++ reg = dwc3_readl(dwc->regs, DWC3_DALEPENA); ++ reg |= DWC3_DALEPENA_EP(dep->number); ++ dwc3_writel(dwc->regs, DWC3_DALEPENA, reg); ++ ++ if (!usb_endpoint_xfer_isoc(desc)) ++ return 0; ++ ++ memset(&trb_link, 0, sizeof(trb_link)); ++ ++ /* Link TRB for ISOC. The HWO but is never reset */ ++ trb_st_hw = &dep->trb_pool[0]; ++ ++ trb_link.bplh = dwc3_trb_dma_offset(dep, trb_st_hw); ++ trb_link.trbctl = DWC3_TRBCTL_LINK_TRB; ++ trb_link.hwo = true; ++ ++ trb_link_hw = &dep->trb_pool[DWC3_TRB_NUM - 1]; ++ dwc3_trb_to_hw(&trb_link, trb_link_hw); ++ } ++ ++ return 0; ++} ++ ++static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum); ++static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep) ++{ ++ struct dwc3_request *req; ++ ++ if (!list_empty(&dep->req_queued)) ++ dwc3_stop_active_transfer(dwc, dep->number); ++ ++ while (!list_empty(&dep->request_list)) { ++ req = next_request(&dep->request_list); ++ ++ dwc3_gadget_giveback(dep, req, -ESHUTDOWN); ++ } ++} ++ ++/** ++ * __dwc3_gadget_ep_disable - Disables a HW endpoint ++ * @dep: the endpoint to disable ++ * ++ * This function also removes requests which are currently processed ny the ++ * hardware and those which are not yet scheduled. ++ * Caller should take care of locking. ++ */ ++static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep) ++{ ++ struct dwc3 *dwc = dep->dwc; ++ u32 reg; ++ ++ dwc3_remove_requests(dwc, dep); ++ ++ reg = dwc3_readl(dwc->regs, DWC3_DALEPENA); ++ reg &= ~DWC3_DALEPENA_EP(dep->number); ++ dwc3_writel(dwc->regs, DWC3_DALEPENA, reg); ++ ++ dep->stream_capable = false; ++ dep->desc = NULL; ++ dep->comp_desc = NULL; ++ dep->type = 0; ++ dep->flags = 0; ++ ++ return 0; ++} ++ ++/* -------------------------------------------------------------------------- */ ++ ++static int dwc3_gadget_ep0_enable(struct usb_ep *ep, ++ const struct usb_endpoint_descriptor *desc) ++{ ++ return -EINVAL; ++} ++ ++static int dwc3_gadget_ep0_disable(struct usb_ep *ep) ++{ ++ return -EINVAL; ++} ++ ++/* -------------------------------------------------------------------------- */ ++ ++static int dwc3_gadget_ep_enable(struct usb_ep *ep, ++ const struct usb_endpoint_descriptor *desc) ++{ ++ struct dwc3_ep *dep; ++ struct dwc3 *dwc; ++ unsigned long flags; ++ int ret; ++ ++ if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) { ++ pr_debug("dwc3: invalid parameters\n"); ++ return -EINVAL; ++ } ++ ++ if (!desc->wMaxPacketSize) { ++ pr_debug("dwc3: missing wMaxPacketSize\n"); ++ return -EINVAL; ++ } ++ ++ dep = to_dwc3_ep(ep); ++ dwc = dep->dwc; ++ ++ switch (usb_endpoint_type(desc)) { ++ case USB_ENDPOINT_XFER_CONTROL: ++ strncat(dep->name, "-control", sizeof(dep->name)); ++ break; ++ case USB_ENDPOINT_XFER_ISOC: ++ strncat(dep->name, "-isoc", sizeof(dep->name)); ++ break; ++ case USB_ENDPOINT_XFER_BULK: ++ strncat(dep->name, "-bulk", sizeof(dep->name)); ++ break; ++ case USB_ENDPOINT_XFER_INT: ++ strncat(dep->name, "-int", sizeof(dep->name)); ++ break; ++ default: ++ dev_err(dwc->dev, "invalid endpoint transfer type\n"); ++ } ++ ++ if (dep->flags & DWC3_EP_ENABLED) { ++ dev_WARN_ONCE(dwc->dev, true, "%s is already enabled\n", ++ dep->name); ++ return 0; ++ } ++ ++ dev_vdbg(dwc->dev, "Enabling %s\n", dep->name); ++ ++ spin_lock_irqsave(&dwc->lock, flags); ++ ret = __dwc3_gadget_ep_enable(dep, desc, NULL /*ep->comp_desc*/); ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ ++ return ret; ++} ++ ++static int dwc3_gadget_ep_disable(struct usb_ep *ep) ++{ ++ struct dwc3_ep *dep; ++ struct dwc3 *dwc; ++ unsigned long flags; ++ int ret; ++ ++ if (!ep) { ++ pr_debug("dwc3: invalid parameters\n"); ++ return -EINVAL; ++ } ++ ++ dep = to_dwc3_ep(ep); ++ dwc = dep->dwc; ++ ++ if (!(dep->flags & DWC3_EP_ENABLED)) { ++ dev_WARN_ONCE(dwc->dev, true, "%s is already disabled\n", ++ dep->name); ++ return 0; ++ } ++ ++ snprintf(dep->name, sizeof(dep->name), "ep%d%s", ++ dep->number >> 1, ++ (dep->number & 1) ? "in" : "out"); ++ ++ spin_lock_irqsave(&dwc->lock, flags); ++ ret = __dwc3_gadget_ep_disable(dep); ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ ++ return ret; ++} ++ ++static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep, ++ gfp_t gfp_flags) ++{ ++ struct dwc3_request *req; ++ struct dwc3_ep *dep = to_dwc3_ep(ep); ++ ++ req = kzalloc(sizeof(*req), gfp_flags); ++ if (!req) { ++ dev_err(dwc->dev, "not enough memory\n"); ++ return NULL; ++ } ++ ++ req->epnum = dep->number; ++ req->dep = dep; ++ req->request.dma = DMA_ADDR_INVALID; ++ ++ return &req->request; ++} ++ ++static void dwc3_gadget_ep_free_request(struct usb_ep *ep, ++ struct usb_request *request) ++{ ++ struct dwc3_request *req = to_dwc3_request(request); ++ ++ kfree(req); ++} ++ ++/* ++ * dwc3_prepare_trbs - setup TRBs from requests ++ * @dep: endpoint for which requests are being prepared ++ * @starting: true if the endpoint is idle and no requests are queued. ++ * ++ * The functions goes through the requests list and setups TRBs for the ++ * transfers. The functions returns once there are not more TRBs available or ++ * it run out of requests. ++ */ ++static struct dwc3_request *dwc3_prepare_trbs(struct dwc3_ep *dep, ++ bool starting) ++{ ++ struct dwc3_request *req, *n, *ret = NULL; ++ struct dwc3_trb_hw *trb_hw; ++ struct dwc3_trb trb; ++ u32 trbs_left; ++ ++ BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM); ++ ++ /* the first request must not be queued */ ++ trbs_left = (dep->busy_slot - dep->free_slot) & DWC3_TRB_MASK; ++ /* ++ * if busy & slot are equal than it is either full or empty. If we are ++ * starting to proceed requests then we are empty. Otherwise we ar ++ * full and don't do anything ++ */ ++ if (!trbs_left) { ++ if (!starting) ++ return NULL; ++ trbs_left = DWC3_TRB_NUM; ++ /* ++ * In case we start from scratch, we queue the ISOC requests ++ * starting from slot 1. This is done because we use ring ++ * buffer and have no LST bit to stop us. Instead, we place ++ * IOC bit TRB_NUM/4. We try to avoid to having an interrupt ++ * after the first request so we start at slot 1 and have ++ * 7 requests proceed before we hit the first IOC. ++ * Other transfer types don't use the ring buffer and are ++ * processed from the first TRB until the last one. Since we ++ * don't wrap around we have to start at the beginning. ++ */ ++ if (usb_endpoint_xfer_isoc(dep->desc)) { ++ dep->busy_slot = 1; ++ dep->free_slot = 1; ++ } else { ++ dep->busy_slot = 0; ++ dep->free_slot = 0; ++ } ++ } ++ ++ /* The last TRB is a link TRB, not used for xfer */ ++ if ((trbs_left <= 1) && usb_endpoint_xfer_isoc(dep->desc)) ++ return NULL; ++ ++ list_for_each_entry_safe(req, n, &dep->request_list, list) { ++ unsigned int last_one = 0; ++ unsigned int cur_slot; ++ ++ trb_hw = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK]; ++ cur_slot = dep->free_slot; ++ dep->free_slot++; ++ ++ /* Skip the LINK-TRB on ISOC */ ++ if (((cur_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) && ++ usb_endpoint_xfer_isoc(dep->desc)) ++ continue; ++ ++ dwc3_gadget_move_request_queued(req); ++ memset(&trb, 0, sizeof(trb)); ++ trbs_left--; ++ ++ /* Is our TRB pool empty? */ ++ if (!trbs_left) ++ last_one = 1; ++ /* Is this the last request? */ ++ if (list_empty(&dep->request_list)) ++ last_one = 1; ++ ++ /* ++ * FIXME we shouldn't need to set LST bit always but we are ++ * facing some weird problem with the Hardware where it doesn't ++ * complete even though it has been previously started. ++ * ++ * While we're debugging the problem, as a workaround to ++ * multiple TRBs handling, use only one TRB at a time. ++ */ ++ last_one = 1; ++ ++ req->trb = trb_hw; ++ if (!ret) ++ ret = req; ++ ++ trb.bplh = req->request.dma; ++ ++ if (usb_endpoint_xfer_isoc(dep->desc)) { ++ trb.isp_imi = true; ++ trb.csp = true; ++ } else { ++ trb.lst = last_one; ++ } ++ ++ if (usb_endpoint_xfer_bulk(dep->desc) && dep->stream_capable) ++ trb.sid_sofn = req->request.stream_id; ++ ++ switch (usb_endpoint_type(dep->desc)) { ++ case USB_ENDPOINT_XFER_CONTROL: ++ trb.trbctl = DWC3_TRBCTL_CONTROL_SETUP; ++ break; ++ ++ case USB_ENDPOINT_XFER_ISOC: ++ trb.trbctl = DWC3_TRBCTL_ISOCHRONOUS_FIRST; ++ ++ /* IOC every DWC3_TRB_NUM / 4 so we can refill */ ++ if (!(cur_slot % (DWC3_TRB_NUM / 4))) ++ trb.ioc = last_one; ++ break; ++ ++ case USB_ENDPOINT_XFER_BULK: ++ case USB_ENDPOINT_XFER_INT: ++ trb.trbctl = DWC3_TRBCTL_NORMAL; ++ break; ++ default: ++ /* ++ * This is only possible with faulty memory because we ++ * checked it already :) ++ */ ++ BUG(); ++ } ++ ++ trb.length = req->request.length; ++ trb.hwo = true; ++ ++ dwc3_trb_to_hw(&trb, trb_hw); ++ req->trb_dma = dwc3_trb_dma_offset(dep, trb_hw); ++ ++ if (last_one) ++ break; ++ } ++ ++ return ret; ++} ++ ++static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param, ++ int start_new) ++{ ++ struct dwc3_gadget_ep_cmd_params params; ++ struct dwc3_request *req; ++ struct dwc3 *dwc = dep->dwc; ++ int ret; ++ u32 cmd; ++ ++ if (start_new && (dep->flags & DWC3_EP_BUSY)) { ++ dev_vdbg(dwc->dev, "%s: endpoint busy\n", dep->name); ++ return -EBUSY; ++ } ++ dep->flags &= ~DWC3_EP_PENDING_REQUEST; ++ ++ /* ++ * If we are getting here after a short-out-packet we don't enqueue any ++ * new requests as we try to set the IOC bit only on the last request. ++ */ ++ if (start_new) { ++ if (list_empty(&dep->req_queued)) ++ dwc3_prepare_trbs(dep, start_new); ++ ++ /* req points to the first request which will be sent */ ++ req = next_request(&dep->req_queued); ++ } else { ++ /* ++ * req points to the first request where HWO changed ++ * from 0 to 1 ++ */ ++ req = dwc3_prepare_trbs(dep, start_new); ++ } ++ if (!req) { ++ dep->flags |= DWC3_EP_PENDING_REQUEST; ++ return 0; ++ } ++ ++ memset(¶ms, 0, sizeof(params)); ++ params.param0 = upper_32_bits(req->trb_dma); ++ params.param1 = lower_32_bits(req->trb_dma); ++ ++ if (start_new) ++ cmd = DWC3_DEPCMD_STARTTRANSFER; ++ else ++ cmd = DWC3_DEPCMD_UPDATETRANSFER; ++ ++ cmd |= DWC3_DEPCMD_PARAM(cmd_param); ++ ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); ++ if (ret < 0) { ++ dev_dbg(dwc->dev, "failed to send STARTTRANSFER command\n"); ++ ++ /* ++ * FIXME we need to iterate over the list of requests ++ * here and stop, unmap, free and del each of the linked ++ * requests instead of we do now. ++ */ ++ dwc3_unmap_buffer_from_dma(req); ++ list_del(&req->list); ++ return ret; ++ } ++ ++ dep->flags |= DWC3_EP_BUSY; ++ dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc, ++ dep->number); ++ return 0; ++} ++ ++static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) ++{ ++ req->request.actual = 0; ++ req->request.status = -EINPROGRESS; ++ req->direction = dep->direction; ++ req->epnum = dep->number; ++ ++ /* ++ * We only add to our list of requests now and ++ * start consuming the list once we get XferNotReady ++ * IRQ. ++ * ++ * That way, we avoid doing anything that we don't need ++ * to do now and defer it until the point we receive a ++ * particular token from the Host side. ++ * ++ * This will also avoid Host cancelling URBs due to too ++ * many NACKs. ++ */ ++ dwc3_map_buffer_to_dma(req); ++ list_add_tail(&req->list, &dep->request_list); ++ ++ /* ++ * There is one special case: XferNotReady with ++ * empty list of requests. We need to kick the ++ * transfer here in that situation, otherwise ++ * we will be NAKing forever. ++ * ++ * If we get XferNotReady before gadget driver ++ * has a chance to queue a request, we will ACK ++ * the IRQ but won't be able to receive the data ++ * until the next request is queued. The following ++ * code is handling exactly that. ++ */ ++ if (dep->flags & DWC3_EP_PENDING_REQUEST) { ++ int ret; ++ int start_trans; ++ ++ start_trans = 1; ++ if (usb_endpoint_xfer_isoc(dep->desc) && ++ dep->flags & DWC3_EP_BUSY) ++ start_trans = 0; ++ ++ ret = __dwc3_gadget_kick_transfer(dep, 0, start_trans); ++ if (ret && ret != -EBUSY) { ++ struct dwc3 *dwc = dep->dwc; ++ ++ dev_dbg(dwc->dev, "%s: failed to kick transfers\n", ++ dep->name); ++ } ++ }; ++ ++ return 0; ++} ++ ++static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, ++ gfp_t gfp_flags) ++{ ++ struct dwc3_request *req = to_dwc3_request(request); ++ struct dwc3_ep *dep = to_dwc3_ep(ep); ++ struct dwc3 *dwc = dep->dwc; ++ ++ unsigned long flags; ++ ++ int ret; ++ ++ if (!dep->desc) { ++ dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n", ++ request, ep->name); ++ return -ESHUTDOWN; ++ } ++ ++ dev_vdbg(dwc->dev, "queing request %p to %s length %d\n", ++ request, ep->name, request->length); ++ ++ spin_lock_irqsave(&dwc->lock, flags); ++ ret = __dwc3_gadget_ep_queue(dep, req); ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ ++ return ret; ++} ++ ++static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, ++ struct usb_request *request) ++{ ++ struct dwc3_request *req = to_dwc3_request(request); ++ struct dwc3_request *r = NULL; ++ ++ struct dwc3_ep *dep = to_dwc3_ep(ep); ++ struct dwc3 *dwc = dep->dwc; ++ ++ unsigned long flags; ++ int ret = 0; ++ ++ spin_lock_irqsave(&dwc->lock, flags); ++ ++ list_for_each_entry(r, &dep->request_list, list) { ++ if (r == req) ++ break; ++ } ++ ++ if (r != req) { ++ list_for_each_entry(r, &dep->req_queued, list) { ++ if (r == req) ++ break; ++ } ++ if (r == req) { ++ /* wait until it is processed */ ++ dwc3_stop_active_transfer(dwc, dep->number); ++ goto out0; ++ } ++ dev_err(dwc->dev, "request %p was not queued to %s\n", ++ request, ep->name); ++ ret = -EINVAL; ++ goto out0; ++ } ++ ++ /* giveback the request */ ++ dwc3_gadget_giveback(dep, req, -ECONNRESET); ++ ++out0: ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ ++ return ret; ++} ++ ++int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value) ++{ ++ struct dwc3_gadget_ep_cmd_params params; ++ struct dwc3 *dwc = dep->dwc; ++ int ret; ++ ++ memset(¶ms, 0x00, sizeof(params)); ++ ++ if (value) { ++ if (dep->number == 0 || dep->number == 1) { ++ /* ++ * Whenever EP0 is stalled, we will restart ++ * the state machine, thus moving back to ++ * Setup Phase ++ */ ++ dwc->ep0state = EP0_SETUP_PHASE; ++ } ++ ++ ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, ++ DWC3_DEPCMD_SETSTALL, ¶ms); ++ if (ret) ++ dev_err(dwc->dev, "failed to %s STALL on %s\n", ++ value ? "set" : "clear", ++ dep->name); ++ else ++ dep->flags |= DWC3_EP_STALL; ++ } else { ++ if (dep->flags & DWC3_EP_WEDGE) ++ return 0; ++ ++ ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, ++ DWC3_DEPCMD_CLEARSTALL, ¶ms); ++ if (ret) ++ dev_err(dwc->dev, "failed to %s STALL on %s\n", ++ value ? "set" : "clear", ++ dep->name); ++ else ++ dep->flags &= ~DWC3_EP_STALL; ++ } ++ ++ return ret; ++} ++ ++static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value) ++{ ++ struct dwc3_ep *dep = to_dwc3_ep(ep); ++ ++ unsigned long flags; ++ ++ int ret; ++ ++ spin_lock_irqsave(&dwc->lock, flags); ++ ++ if (usb_endpoint_xfer_isoc(dep->desc)) { ++ dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ ret = __dwc3_gadget_ep_set_halt(dep, value); ++out: ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ ++ return ret; ++} ++ ++/* -------------------------------------------------------------------------- */ ++ ++static struct usb_endpoint_descriptor dwc3_gadget_ep0_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bmAttributes = USB_ENDPOINT_XFER_CONTROL, ++}; ++ ++static const struct usb_ep_ops dwc3_gadget_ep0_ops = { ++ .enable = dwc3_gadget_ep0_enable, ++ .disable = dwc3_gadget_ep0_disable, ++ .alloc_request = dwc3_gadget_ep_alloc_request, ++ .free_request = dwc3_gadget_ep_free_request, ++ .queue = dwc3_gadget_ep0_queue, ++ .dequeue = dwc3_gadget_ep_dequeue, ++ .set_halt = dwc3_gadget_ep_set_halt, ++}; ++ ++static const struct usb_ep_ops dwc3_gadget_ep_ops = { ++ .enable = dwc3_gadget_ep_enable, ++ .disable = dwc3_gadget_ep_disable, ++ .alloc_request = dwc3_gadget_ep_alloc_request, ++ .free_request = dwc3_gadget_ep_free_request, ++ .queue = dwc3_gadget_ep_queue, ++ .dequeue = dwc3_gadget_ep_dequeue, ++ .set_halt = dwc3_gadget_ep_set_halt, ++}; ++ ++/* -------------------------------------------------------------------------- */ ++ ++static int dwc3_gadget_get_frame(struct usb_gadget *g) ++{ ++ struct dwc3 *dwc = gadget_to_dwc(g); ++ u32 reg; ++ ++ reg = dwc3_readl(dwc->regs, DWC3_DSTS); ++ return DWC3_DSTS_SOFFN(reg); ++} ++ ++static int dwc3_gadget_wakeup(struct usb_gadget *g) ++{ ++ struct dwc3 *dwc = gadget_to_dwc(g); ++ ++ unsigned long timeout; ++ unsigned long flags; ++ ++ u32 reg; ++ ++ int ret = 0; ++ ++ u8 link_state; ++ u8 speed; ++ ++ spin_lock_irqsave(&dwc->lock, flags); ++ ++ /* ++ * According to the Databook Remote wakeup request should ++ * be issued only when the device is in early suspend state. ++ * ++ * We can check that via USB Link State bits in DSTS register. ++ */ ++ reg = dwc3_readl(dwc->regs, DWC3_DSTS); ++ ++ speed = reg & DWC3_DSTS_CONNECTSPD; ++ if (speed == DWC3_DSTS_SUPERSPEED) { ++ dev_dbg(dwc->dev, "no wakeup on SuperSpeed\n"); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ link_state = DWC3_DSTS_USBLNKST(reg); ++ ++ switch (link_state) { ++ case DWC3_LINK_STATE_RX_DET: /* in HS, means Early Suspend */ ++ case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */ ++ break; ++ default: ++ dev_dbg(dwc->dev, "can't wakeup from link state %d\n", ++ link_state); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ reg = dwc3_readl(dwc->regs, DWC3_DCTL); ++ ++ /* ++ * Switch link state to Recovery. In HS/FS/LS this means ++ * RemoteWakeup Request ++ */ ++ reg |= DWC3_DCTL_ULSTCHNG_RECOVERY; ++ dwc3_writel(dwc->regs, DWC3_DCTL, reg); ++ ++ /* wait for at least 2000us */ ++ usleep_range(2000, 2500); ++ ++ /* write zeroes to Link Change Request */ ++ reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; ++ dwc3_writel(dwc->regs, DWC3_DCTL, reg); ++ ++ /* pool until Link State change to ON */ ++ timeout = 100; ++ ++ while (timeout) { ++ reg = dwc3_readl(dwc->regs, DWC3_DSTS); ++ ++ /* in HS, means ON */ ++ if (DWC3_DSTS_USBLNKST(reg) == DWC3_LINK_STATE_U0) ++ break; ++ timeout--; ++ mdelay(1); ++ } ++ ++ if (DWC3_DSTS_USBLNKST(reg) != DWC3_LINK_STATE_U0) { ++ dev_err(dwc->dev, "failed to send remote wakeup\n"); ++ ret = -EINVAL; ++ } ++ ++out: ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ ++ return ret; ++} ++ ++static int dwc3_gadget_set_selfpowered(struct usb_gadget *g, ++ int is_selfpowered) ++{ ++ struct dwc3 *dwc = gadget_to_dwc(g); ++ ++ dwc->is_selfpowered = !!is_selfpowered; ++ ++ return 0; ++} ++ ++static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on) ++{ ++ u32 reg; ++ u32 timeout = 500; ++ ++ reg = dwc3_readl(dwc->regs, DWC3_DCTL); ++ if (is_on) ++ reg |= DWC3_DCTL_RUN_STOP; ++ else ++ reg &= ~DWC3_DCTL_RUN_STOP; ++ ++ dwc3_writel(dwc->regs, DWC3_DCTL, reg); ++ ++ do { ++ reg = dwc3_readl(dwc->regs, DWC3_DSTS); ++ if (is_on) { ++ if (!(reg & DWC3_DSTS_DEVCTRLHLT)) ++ break; ++ } else { ++ if (reg & DWC3_DSTS_DEVCTRLHLT) ++ break; ++ } ++ timeout--; ++ if (!timeout) ++ break; ++ udelay(1); ++ } while (1); ++} ++ ++static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) ++{ ++ struct dwc3 *dwc = gadget_to_dwc(g); ++ unsigned long flags; ++ ++ is_on = !!is_on; ++ ++ spin_lock_irqsave(&dwc->lock, flags); ++ dwc3_gadget_run_stop(dwc, is_on); ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ ++ return 0; ++} ++ ++static int dwc3_gadget_start(struct usb_gadget *g, ++ struct usb_gadget_driver *driver) ++{ ++ struct dwc3 *dwc = gadget_to_dwc(g); ++ struct dwc3_ep *dep; ++ unsigned long flags; ++ int ret = 0; ++ u32 reg; ++ ++ spin_lock_irqsave(&dwc->lock, flags); ++ ++ if (dwc->gadget_driver) { ++ dev_err(dwc->dev, "%s is already bound\n", ++ dwc->gadget.name); ++ ret = -EBUSY; ++ goto err0; ++ } ++ ++ dwc->gadget_driver = driver; ++ /* dwc->gadget.dev.driver = &driver->driver; */ ++ ++ reg = dwc3_readl(dwc->regs, DWC3_DCFG); ++ reg &= ~(DWC3_DCFG_SPEED_MASK); ++ reg |= dwc->maximum_speed; ++ dwc3_writel(dwc->regs, DWC3_DCFG, reg); ++ ++ dwc->start_config_issued = false; ++ ++ /* Start with SuperSpeed Default */ ++ dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); ++ ++ dep = dwc->eps[0]; ++ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL); ++ if (ret) { ++ dev_err(dwc->dev, "failed to enable %s\n", dep->name); ++ goto err0; ++ } ++ ++ dep = dwc->eps[1]; ++ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL); ++ if (ret) { ++ dev_err(dwc->dev, "failed to enable %s\n", dep->name); ++ goto err1; ++ } ++ ++ /* begin to receive SETUP packets */ ++ dwc->ep0state = EP0_SETUP_PHASE; ++ dwc3_ep0_out_start(dwc); ++ ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ ++ return 0; ++ ++err1: ++ __dwc3_gadget_ep_disable(dwc->eps[0]); ++ ++err0: ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ ++ return ret; ++} ++ ++#if 0 ++static int dwc3_gadget_stop(struct usb_gadget *g, ++ struct usb_gadget_driver *driver) ++{ ++ struct dwc3 *dwc = gadget_to_dwc(g); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&dwc->lock, flags); ++ ++ __dwc3_gadget_ep_disable(dwc->eps[0]); ++ __dwc3_gadget_ep_disable(dwc->eps[1]); ++ ++ dwc->gadget_driver = NULL; ++ ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ ++ return 0; ++} ++#endif ++static const struct usb_gadget_ops dwc3_gadget_ops = { ++ .get_frame = dwc3_gadget_get_frame, ++ .wakeup = dwc3_gadget_wakeup, ++ .set_selfpowered = dwc3_gadget_set_selfpowered, ++ .pullup = dwc3_gadget_pullup, ++ ++ ++}; ++ ++int usb_gadget_probe_driver(struct usb_gadget_driver *driver, ++ int (*bind)(struct usb_gadget *)) ++{ ++ int ret; ++ ++ ret = bind(&global_dwc3->gadget); ++ if (ret) ++ return ret; ++ ret = dwc3_gadget_start(&global_dwc3->gadget, driver); ++ if (ret) ++ return ret; ++ ret = usb_gadget_connect(&global_dwc3->gadget); ++ if (ret) ++ return ret; ++ return 0; ++} ++ ++/* -------------------------------------------------------------------------- */ ++ ++static int __devinit dwc3_gadget_init_endpoints(struct dwc3 *dwc) ++{ ++ struct dwc3_ep *dep; ++ u8 epnum; ++ ++ INIT_LIST_HEAD(&dwc->gadget.ep_list); ++ ++ for (epnum = 0; epnum < DWC3_ENDPOINTS_NUM; epnum++) { ++ dep = kzalloc(sizeof(*dep), GFP_KERNEL); ++ if (!dep) { ++ dev_err(dwc->dev, "can't allocate endpoint %d\n", ++ epnum); ++ return -ENOMEM; ++ } ++ ++ dep->dwc = dwc; ++ dep->number = epnum; ++ dwc->eps[epnum] = dep; ++ ++ snprintf(dep->name, sizeof(dep->name), "ep%d%s", epnum >> 1, ++ (epnum & 1) ? "in" : "out"); ++ dep->endpoint.name = dep->name; ++ dep->direction = (epnum & 1); ++ ++ if (epnum == 0 || epnum == 1) { ++ dep->endpoint.maxpacket = 64; ++ dep->endpoint.ops = &dwc3_gadget_ep0_ops; ++ if (!epnum) ++ dwc->gadget.ep0 = &dep->endpoint; ++ } else { ++ int ret; ++ ++ dep->endpoint.maxpacket = 1024; ++ dep->endpoint.ops = &dwc3_gadget_ep_ops; ++ list_add_tail(&dep->endpoint.ep_list, ++ &dwc->gadget.ep_list); ++ ++ ret = dwc3_alloc_trb_pool(dep); ++ if (ret) ++ return ret; ++ } ++ ++ INIT_LIST_HEAD(&dep->request_list); ++ INIT_LIST_HEAD(&dep->req_queued); ++ } ++ ++ return 0; ++} ++ ++static void dwc3_gadget_free_endpoints(struct dwc3 *dwc) ++{ ++ struct dwc3_ep *dep; ++ u8 epnum; ++ ++ for (epnum = 0; epnum < DWC3_ENDPOINTS_NUM; epnum++) { ++ dep = dwc->eps[epnum]; ++ dwc3_free_trb_pool(dep); ++ ++ if (epnum != 0 && epnum != 1) ++ list_del(&dep->endpoint.ep_list); ++ ++ kfree(dep); ++ } ++} ++ ++static struct dwc3 *the_dwc; ++ ++/* -------------------------------------------------------------------------- */ ++static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, ++ const struct dwc3_event_depevt *event, int status) ++{ ++ struct dwc3_request *req; ++ struct dwc3_trb trb; ++ unsigned int count; ++ unsigned int s_pkt = 0; ++ ++ do { ++ req = next_request(&dep->req_queued); ++ if (!req) { ++ WARN_ON_ONCE(1); ++ return 1; ++ } ++ ++ dwc3_trb_to_nat(req->trb, &trb); ++ ++ if (trb.hwo && status != -ESHUTDOWN) ++ /* ++ * We continue despite the error. There is not much we ++ * can do. If we don't clean in up we loop for ever. If ++ * we skip the TRB than it gets overwritten reused after ++ * a while since we use them in a ring buffer. a BUG() ++ * would help. Lets hope that if this occures, someone ++ * fixes the root cause instead of looking away :) ++ */ ++ dev_err(dwc->dev, "%s's TRB (%p) still owned by HW\n", ++ dep->name, req->trb); ++ count = trb.length; ++ ++ if (dep->direction) { ++ if (count) { ++ dev_err(dwc->dev, "incomplete IN transfer %s\n", ++ dep->name); ++ status = -ECONNRESET; ++ } ++ } else { ++ if (count && (event->status & DEPEVT_STATUS_SHORT)) ++ s_pkt = 1; ++ } ++ ++ /* ++ * We assume here we will always receive the entire data block ++ * which we should receive. Meaning, if we program RX to ++ * receive 4K but we receive only 2K, we assume that's all we ++ * should receive and we simply bounce the request back to the ++ * gadget driver for further processing. ++ */ ++ req->request.actual += req->request.length - count; ++ dwc3_gadget_giveback(dep, req, status); ++ if (s_pkt) ++ break; ++ if ((event->status & DEPEVT_STATUS_LST) && trb.lst) ++ break; ++ if ((event->status & DEPEVT_STATUS_IOC) && trb.ioc) ++ break; ++ } while (1); ++ ++ if ((event->status & DEPEVT_STATUS_IOC) && trb.ioc) ++ return 0; ++ return 1; ++} ++ ++static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc, ++ struct dwc3_ep *dep, const struct dwc3_event_depevt *event, ++ int start_new) ++{ ++ unsigned status = 0; ++ int clean_busy; ++ ++ if (event->status & DEPEVT_STATUS_BUSERR) ++ status = -ECONNRESET; ++ ++ clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status); ++ if (clean_busy) { ++ dep->flags &= ~DWC3_EP_BUSY; ++ dep->res_trans_idx = 0; ++ } ++ ++ /* ++ * WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround. ++ * See dwc3_gadget_linksts_change_interrupt() for 1st half. ++ */ ++ if (dwc->revision < DWC3_REVISION_183A) { ++ u32 reg; ++ int i; ++ ++ for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) { ++ struct dwc3_ep *dep = dwc->eps[i]; ++ ++ if (!(dep->flags & DWC3_EP_ENABLED)) ++ continue; ++ ++ if (!list_empty(&dep->req_queued)) ++ return; ++ } ++ ++ reg = dwc3_readl(dwc->regs, DWC3_DCTL); ++ reg |= dwc->u1u2; ++ dwc3_writel(dwc->regs, DWC3_DCTL, reg); ++ ++ dwc->u1u2 = 0; ++ } ++} ++ ++static void dwc3_gadget_start_isoc(struct dwc3 *dwc, ++ struct dwc3_ep *dep, const struct dwc3_event_depevt *event) ++{ ++ u32 uf; ++ ++ if (list_empty(&dep->request_list)) { ++ dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n", ++ dep->name); ++ return; ++ } ++ ++ if (event->parameters) { ++ u32 mask; ++ ++ mask = ~(dep->interval - 1); ++ uf = event->parameters & mask; ++ /* 4 micro frames in the future */ ++ uf += dep->interval * 4; ++ } else { ++ uf = 0; ++ } ++ ++ __dwc3_gadget_kick_transfer(dep, uf, 1); ++} ++ ++static void dwc3_process_ep_cmd_complete(struct dwc3_ep *dep, ++ const struct dwc3_event_depevt *event) ++{ ++ struct dwc3 *dwc = dep->dwc; ++ struct dwc3_event_depevt mod_ev = *event; ++ ++ /* ++ * We were asked to remove one requests. It is possible that this ++ * request and a few other were started together and have the same ++ * transfer index. Since we stopped the complete endpoint we don't ++ * know how many requests were already completed (and not yet) ++ * reported and how could be done (later). We purge them all until ++ * the end of the list. ++ */ ++ mod_ev.status = DEPEVT_STATUS_LST; ++ dwc3_cleanup_done_reqs(dwc, dep, &mod_ev, -ESHUTDOWN); ++ dep->flags &= ~DWC3_EP_BUSY; ++ /* pending requets are ignored and are queued on XferNotReady */ ++} ++ ++static void dwc3_ep_cmd_compl(struct dwc3_ep *dep, ++ const struct dwc3_event_depevt *event) ++{ ++ u32 param = event->parameters; ++ u32 cmd_type = (param >> 8) & ((1 << 5) - 1); ++ ++ switch (cmd_type) { ++ case DWC3_DEPCMD_ENDTRANSFER: ++ dwc3_process_ep_cmd_complete(dep, event); ++ break; ++ case DWC3_DEPCMD_STARTTRANSFER: ++ dep->res_trans_idx = param & 0x7f; ++ break; ++ default: ++ break; ++ }; ++} ++ ++static void dwc3_endpoint_interrupt(struct dwc3 *dwc, ++ const struct dwc3_event_depevt *event) ++{ ++ struct dwc3_ep *dep; ++ u8 epnum = event->endpoint_number; ++ ++ dep = dwc->eps[epnum]; ++ ++ dev_vdbg(dwc->dev, "%s: %s\n", dep->name, ++ dwc3_ep_event_string(event->endpoint_event)); ++ ++ if (epnum == 0 || epnum == 1) { ++ dwc3_ep0_interrupt(dwc, event); ++ return; ++ } ++ ++ switch (event->endpoint_event) { ++ case DWC3_DEPEVT_XFERCOMPLETE: ++ if (usb_endpoint_xfer_isoc(dep->desc)) { ++ dev_dbg(dwc->dev, "%s is an Isochronous endpoint\n", ++ dep->name); ++ return; ++ } ++ ++ dwc3_endpoint_transfer_complete(dwc, dep, event, 1); ++ break; ++ case DWC3_DEPEVT_XFERINPROGRESS: ++ if (!usb_endpoint_xfer_isoc(dep->desc)) { ++ dev_dbg(dwc->dev, "%s is not an Isochronous endpoint\n", ++ dep->name); ++ return; ++ } ++ ++ dwc3_endpoint_transfer_complete(dwc, dep, event, 0); ++ break; ++ case DWC3_DEPEVT_XFERNOTREADY: ++ if (usb_endpoint_xfer_isoc(dep->desc)) { ++ dwc3_gadget_start_isoc(dwc, dep, event); ++ } else { ++ int ret; ++ ++ dev_vdbg(dwc->dev, "%s: reason %s\n", ++ dep->name, event->status ++ ? "Transfer Active" ++ : "Transfer Not Active"); ++ ++ ret = __dwc3_gadget_kick_transfer(dep, 0, 1); ++ if (!ret || ret == -EBUSY) ++ return; ++ dev_dbg(dwc->dev, "%s: failed to kick transfers %d.\n", ++ dep->name, ret); ++ } ++ ++ break; ++ case DWC3_DEPEVT_STREAMEVT: ++ if (!usb_endpoint_xfer_bulk(dep->desc)) { ++ dev_err(dwc->dev, "Stream event for non-Bulk %s\n", ++ dep->name); ++ return; ++ } ++ ++ switch (event->status) { ++ case DEPEVT_STREAMEVT_FOUND: ++ dev_vdbg(dwc->dev, "Stream %d found and started\n", ++ event->parameters); ++ ++ break; ++ case DEPEVT_STREAMEVT_NOTFOUND: ++ /* FALLTHROUGH */ ++ default: ++ dev_dbg(dwc->dev, "Couldn't find suitable stream\n"); ++ } ++ break; ++ case DWC3_DEPEVT_RXTXFIFOEVT: ++ dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name); ++ break; ++ case DWC3_DEPEVT_EPCMDCMPLT: ++ dwc3_ep_cmd_compl(dep, event); ++ break; ++ } ++} ++ ++static void dwc3_disconnect_gadget(struct dwc3 *dwc) ++{ ++ if (dwc->gadget_driver && dwc->gadget_driver->disconnect) { ++ spin_unlock(&dwc->lock); ++ dwc->gadget_driver->disconnect(&dwc->gadget); ++ spin_lock(&dwc->lock); ++ } ++} ++ ++static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum) ++{ ++ struct dwc3_ep *dep; ++ struct dwc3_gadget_ep_cmd_params params; ++ u32 cmd; ++ int ret; ++ ++ dep = dwc->eps[epnum]; ++ ++ WARN_ON(!dep->res_trans_idx); ++ if (dep->res_trans_idx) { ++ cmd = DWC3_DEPCMD_ENDTRANSFER; ++ cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC; ++ cmd |= DWC3_DEPCMD_PARAM(dep->res_trans_idx); ++ memset(¶ms, 0, sizeof(params)); ++ ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); ++ WARN_ON_ONCE(ret); ++ dep->res_trans_idx = 0; ++ } ++} ++ ++static void dwc3_stop_active_transfers(struct dwc3 *dwc) ++{ ++ u32 epnum; ++ ++ for (epnum = 2; epnum < DWC3_ENDPOINTS_NUM; epnum++) { ++ struct dwc3_ep *dep; ++ ++ dep = dwc->eps[epnum]; ++ if (!(dep->flags & DWC3_EP_ENABLED)) ++ continue; ++ ++ dwc3_remove_requests(dwc, dep); ++ } ++} ++ ++static void dwc3_clear_stall_all_ep(struct dwc3 *dwc) ++{ ++ u32 epnum; ++ ++ for (epnum = 1; epnum < DWC3_ENDPOINTS_NUM; epnum++) { ++ struct dwc3_ep *dep; ++ struct dwc3_gadget_ep_cmd_params params; ++ int ret; ++ ++ dep = dwc->eps[epnum]; ++ ++ if (!(dep->flags & DWC3_EP_STALL)) ++ continue; ++ ++ dep->flags &= ~DWC3_EP_STALL; ++ ++ memset(¶ms, 0, sizeof(params)); ++ ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, ++ DWC3_DEPCMD_CLEARSTALL, ¶ms); ++ WARN_ON_ONCE(ret); ++ } ++} ++ ++static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) ++{ ++ dev_vdbg(dwc->dev, "%s\n", __func__); ++#if 0 ++ XXX ++ U1/U2 is powersave optimization. Skip it for now. Anyway we need to ++ enable it before we can disable it. ++ ++ reg = dwc3_readl(dwc->regs, DWC3_DCTL); ++ reg &= ~DWC3_DCTL_INITU1ENA; ++ dwc3_writel(dwc->regs, DWC3_DCTL, reg); ++ ++ reg &= ~DWC3_DCTL_INITU2ENA; ++ dwc3_writel(dwc->regs, DWC3_DCTL, reg); ++#endif ++ ++ dwc3_stop_active_transfers(dwc); ++ dwc3_disconnect_gadget(dwc); ++ dwc->start_config_issued = false; ++ ++ dwc->gadget.speed = USB_SPEED_UNKNOWN; ++ dwc->setup_packet_pending = false; ++} ++ ++static void dwc3_gadget_usb3_phy_power(struct dwc3 *dwc, int on) ++{ ++ u32 reg; ++ ++ reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); ++ ++ if (on) ++ reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; ++ else ++ reg |= DWC3_GUSB3PIPECTL_SUSPHY; ++ ++ dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); ++} ++ ++static void dwc3_gadget_usb2_phy_power(struct dwc3 *dwc, int on) ++{ ++ u32 reg; ++ ++ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); ++ ++ if (on) ++ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; ++ else ++ reg |= DWC3_GUSB2PHYCFG_SUSPHY; ++ ++ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); ++} ++ ++static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) ++{ ++ u32 reg; ++ ++ dev_vdbg(dwc->dev, "%s\n", __func__); ++ ++ /* ++ * WORKAROUND: DWC3 revisions <1.88a have an issue which ++ * would cause a missing Disconnect Event if there's a ++ * pending Setup Packet in the FIFO. ++ * ++ * There's no suggested workaround on the official Bug ++ * report, which states that "unless the driver/application ++ * is doing any special handling of a disconnect event, ++ * there is no functional issue". ++ * ++ * Unfortunately, it turns out that we _do_ some special ++ * handling of a disconnect event, namely complete all ++ * pending transfers, notify gadget driver of the ++ * disconnection, and so on. ++ * ++ * Our suggested workaround is to follow the Disconnect ++ * Event steps here, instead, based on a setup_packet_pending ++ * flag. Such flag gets set whenever we have a XferNotReady ++ * event on EP0 and gets cleared on XferComplete for the ++ * same endpoint. ++ * ++ * Refers to: ++ * ++ * STAR#9000466709: RTL: Device : Disconnect event not ++ * generated if setup packet pending in FIFO ++ */ ++ if (dwc->revision < DWC3_REVISION_188A) { ++ if (dwc->setup_packet_pending) ++ dwc3_gadget_disconnect_interrupt(dwc); ++ } ++ ++ /* Enable PHYs */ ++ dwc3_gadget_usb2_phy_power(dwc, true); ++ dwc3_gadget_usb3_phy_power(dwc, true); ++ ++ if (dwc->gadget.speed != USB_SPEED_UNKNOWN) ++ dwc3_disconnect_gadget(dwc); ++ ++ reg = dwc3_readl(dwc->regs, DWC3_DCTL); ++ reg &= ~DWC3_DCTL_TSTCTRL_MASK; ++ dwc3_writel(dwc->regs, DWC3_DCTL, reg); ++ ++ dwc3_stop_active_transfers(dwc); ++ dwc3_clear_stall_all_ep(dwc); ++ dwc->start_config_issued = false; ++ ++ /* Reset device address to zero */ ++ reg = dwc3_readl(dwc->regs, DWC3_DCFG); ++ reg &= ~(DWC3_DCFG_DEVADDR_MASK); ++ dwc3_writel(dwc->regs, DWC3_DCFG, reg); ++} ++ ++static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed) ++{ ++ u32 reg; ++ u32 usb30_clock = DWC3_GCTL_CLK_BUS; ++ ++ /* ++ * We change the clock only at SS but I dunno why I would want to do ++ * this. Maybe it becomes part of the power saving plan. ++ */ ++ ++ if (speed != DWC3_DSTS_SUPERSPEED) ++ return; ++ ++ /* ++ * RAMClkSel is reset to 0 after USB reset, so it must be reprogrammed ++ * each time on Connect Done. ++ */ ++ if (!usb30_clock) ++ return; ++ ++ reg = dwc3_readl(dwc->regs, DWC3_GCTL); ++ reg |= DWC3_GCTL_RAMCLKSEL(usb30_clock); ++ dwc3_writel(dwc->regs, DWC3_GCTL, reg); ++} ++ ++static void dwc3_gadget_disable_phy(struct dwc3 *dwc, u8 speed) ++{ ++ switch (speed) { ++ case USB_SPEED_SUPER: ++ dwc3_gadget_usb2_phy_power(dwc, false); ++ break; ++ case USB_SPEED_HIGH: ++ case USB_SPEED_FULL: ++ case USB_SPEED_LOW: ++ dwc3_gadget_usb3_phy_power(dwc, false); ++ break; ++ } ++} ++ ++static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) ++{ ++ struct dwc3_gadget_ep_cmd_params params; ++ struct dwc3_ep *dep; ++ int ret; ++ u32 reg; ++ u8 speed; ++ ++ dev_vdbg(dwc->dev, "%s\n", __func__); ++ ++ memset(¶ms, 0x00, sizeof(params)); ++ ++ reg = dwc3_readl(dwc->regs, DWC3_DSTS); ++ speed = reg & DWC3_DSTS_CONNECTSPD; ++ dwc->speed = speed; ++ ++ dwc3_update_ram_clk_sel(dwc, speed); ++ ++ switch (speed) { ++ case DWC3_DCFG_SUPERSPEED: ++ /* ++ * WORKAROUND: DWC3 revisions <1.90a have an issue which ++ * would cause a missing USB3 Reset event. ++ * ++ * In such situations, we should force a USB3 Reset ++ * event by calling our dwc3_gadget_reset_interrupt() ++ * routine. ++ * ++ * Refers to: ++ * ++ * STAR#9000483510: RTL: SS : USB3 reset event may ++ * not be generated always when the link enters poll ++ */ ++ if (dwc->revision < DWC3_REVISION_190A) ++ dwc3_gadget_reset_interrupt(dwc); ++ ++ dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); ++ dwc->gadget.ep0->maxpacket = 512; ++ dwc->gadget.speed = USB_SPEED_SUPER; ++ break; ++ case DWC3_DCFG_HIGHSPEED: ++ dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64); ++ dwc->gadget.ep0->maxpacket = 64; ++ dwc->gadget.speed = USB_SPEED_HIGH; ++ break; ++ case DWC3_DCFG_FULLSPEED2: ++ case DWC3_DCFG_FULLSPEED1: ++ dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64); ++ dwc->gadget.ep0->maxpacket = 64; ++ dwc->gadget.speed = USB_SPEED_FULL; ++ break; ++ case DWC3_DCFG_LOWSPEED: ++ dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8); ++ dwc->gadget.ep0->maxpacket = 8; ++ dwc->gadget.speed = USB_SPEED_LOW; ++ break; ++ } ++ ++ /* Disable unneded PHY */ ++ dwc3_gadget_disable_phy(dwc, dwc->gadget.speed); ++ ++ dep = dwc->eps[0]; ++ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL); ++ if (ret) { ++ dev_err(dwc->dev, "failed to enable %s\n", dep->name); ++ return; ++ } ++ ++ dep = dwc->eps[1]; ++ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL); ++ if (ret) { ++ dev_err(dwc->dev, "failed to enable %s\n", dep->name); ++ return; ++ } ++ ++ /* ++ * Configure PHY via GUSB3PIPECTLn if required. ++ * ++ * Update GTXFIFOSIZn ++ * ++ * In both cases reset values should be sufficient. ++ */ ++} ++ ++static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc) ++{ ++ dev_vdbg(dwc->dev, "%s\n", __func__); ++ ++ /* ++ * TODO take core out of low power mode when that's ++ * implemented. ++ */ ++ ++ dwc->gadget_driver->resume(&dwc->gadget); ++} ++ ++static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, ++ unsigned int evtinfo) ++{ ++ enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK; ++ ++ /* ++ * WORKAROUND: DWC3 Revisions <1.83a have an issue which, depending ++ * on the link partner, the USB session might do multiple entry/exit ++ * of low power states before a transfer takes place. ++ * ++ * Due to this problem, we might experience lower throughput. The ++ * suggested workaround is to disable DCTL[12:9] bits if we're ++ * transitioning from U1/U2 to U0 and enable those bits again ++ * after a transfer completes and there are no pending transfers ++ * on any of the enabled endpoints. ++ * ++ * This is the first half of that workaround. ++ * ++ * Refers to: ++ * ++ * STAR#9000446952: RTL: Device SS : if U1/U2 ->U0 takes >128us ++ * core send LGO_Ux entering U0 ++ */ ++ if (dwc->revision < DWC3_REVISION_183A) { ++ if (next == DWC3_LINK_STATE_U0) { ++ u32 u1u2; ++ u32 reg; ++ ++ switch (dwc->link_state) { ++ case DWC3_LINK_STATE_U1: ++ case DWC3_LINK_STATE_U2: ++ reg = dwc3_readl(dwc->regs, DWC3_DCTL); ++ u1u2 = reg & (DWC3_DCTL_INITU2ENA ++ | DWC3_DCTL_ACCEPTU2ENA ++ | DWC3_DCTL_INITU1ENA ++ | DWC3_DCTL_ACCEPTU1ENA); ++ ++ if (!dwc->u1u2) ++ dwc->u1u2 = reg & u1u2; ++ ++ reg &= ~u1u2; ++ ++ dwc3_writel(dwc->regs, DWC3_DCTL, reg); ++ break; ++ default: ++ /* do nothing */ ++ break; ++ } ++ } ++ } ++ ++ dwc->link_state = next; ++ ++ dev_vdbg(dwc->dev, "%s link %d\n", __func__, dwc->link_state); ++} ++ ++static void dwc3_gadget_interrupt(struct dwc3 *dwc, ++ const struct dwc3_event_devt *event) ++{ ++ switch (event->type) { ++ case DWC3_DEVICE_EVENT_DISCONNECT: ++ dwc3_gadget_disconnect_interrupt(dwc); ++ break; ++ case DWC3_DEVICE_EVENT_RESET: ++ dwc3_gadget_reset_interrupt(dwc); ++ break; ++ case DWC3_DEVICE_EVENT_CONNECT_DONE: ++ dwc3_gadget_conndone_interrupt(dwc); ++ break; ++ case DWC3_DEVICE_EVENT_WAKEUP: ++ dwc3_gadget_wakeup_interrupt(dwc); ++ break; ++ case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: ++ dwc3_gadget_linksts_change_interrupt(dwc, event->event_info); ++ break; ++ case DWC3_DEVICE_EVENT_EOPF: ++ dev_vdbg(dwc->dev, "End of Periodic Frame\n"); ++ break; ++ case DWC3_DEVICE_EVENT_SOF: ++ dev_vdbg(dwc->dev, "Start of Periodic Frame\n"); ++ break; ++ case DWC3_DEVICE_EVENT_ERRATIC_ERROR: ++ dev_vdbg(dwc->dev, "Erratic Error\n"); ++ break; ++ case DWC3_DEVICE_EVENT_CMD_CMPL: ++ dev_vdbg(dwc->dev, "Command Complete\n"); ++ break; ++ case DWC3_DEVICE_EVENT_OVERFLOW: ++ dev_vdbg(dwc->dev, "Overflow\n"); ++ break; ++ default: ++ dev_dbg(dwc->dev, "UNKNOWN IRQ %d\n", event->type); ++ } ++} ++ ++static void dwc3_process_event_entry(struct dwc3 *dwc, ++ const union dwc3_event *event) ++{ ++ /* Endpoint IRQ, handle it and return early */ ++ if (event->type.is_devspec == 0) { ++ /* depevt */ ++ return dwc3_endpoint_interrupt(dwc, &event->depevt); ++ } ++ ++ switch (event->type.type) { ++ case DWC3_EVENT_TYPE_DEV: ++ dwc3_gadget_interrupt(dwc, &event->devt); ++ break; ++ /* REVISIT what to do with Carkit and I2C events ? */ ++ default: ++ dev_err(dwc->dev, "UNKNOWN IRQ type %d\n", event->raw); ++ } ++} ++ ++static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf) ++{ ++ struct dwc3_event_buffer *evt; ++ int left; ++ u32 count; ++ ++ count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(buf)); ++ count &= DWC3_GEVNTCOUNT_MASK; ++ if (!count) ++ return IRQ_NONE; ++ ++ evt = dwc->ev_buffs[buf]; ++ left = count; ++ ++ while (left > 0) { ++ union dwc3_event event; ++ ++ memcpy(&event.raw, (evt->buf + evt->lpos), sizeof(event.raw)); ++ dwc3_process_event_entry(dwc, &event); ++ /* ++ * XXX we wrap around correctly to the next entry as almost all ++ * entries are 4 bytes in size. There is one entry which has 12 ++ * bytes which is a regular entry followed by 8 bytes data. ATM ++ * I don't know how things are organized if were get next to the ++ * a boundary so I worry about that once we try to handle that. ++ */ ++ evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE; ++ left -= 4; ++ ++ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), 4); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++irqreturn_t dwc3_interrupt(int irq, void *_dwc) ++{ ++ struct dwc3 *dwc = _dwc; ++ int i; ++ irqreturn_t ret = IRQ_NONE; ++ ++ spin_lock(&dwc->lock); ++ ++ for (i = 0; i < dwc->num_event_buffers; i++) { ++ irqreturn_t status; ++ ++ status = dwc3_process_event_buf(dwc, i); ++ if (status == IRQ_HANDLED) ++ ret = status; ++ } ++ ++ spin_unlock(&dwc->lock); ++ ++ return ret; ++} ++ ++/** ++ * dwc3_gadget_init - Initializes gadget related registers ++ * @dwc: Pointer to out controller context structure ++ * ++ * Returns 0 on success otherwise negative errno. ++ */ ++int __devinit dwc3_gadget_init(struct dwc3 *dwc) ++{ ++ u32 reg; ++ int ret; ++ ++ dwc->ctrl_req = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ctrl_req), ++ &dwc->ctrl_req_addr, GFP_KERNEL); ++ if (!dwc->ctrl_req) { ++ dev_err(dwc->dev, "failed to allocate ctrl request\n"); ++ ret = -ENOMEM; ++ goto err0; ++ } ++ ++ dwc->ep0_trb = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ep0_trb), ++ &dwc->ep0_trb_addr, GFP_KERNEL); ++ if (!dwc->ep0_trb) { ++ dev_err(dwc->dev, "failed to allocate ep0 trb\n"); ++ ret = -ENOMEM; ++ goto err1; ++ } ++ ++ dwc->setup_buf = dma_alloc_coherent(dwc->dev, ++ sizeof(*dwc->setup_buf) * 2, ++ &dwc->setup_buf_addr, GFP_KERNEL); ++ if (!dwc->setup_buf) { ++ dev_err(dwc->dev, "failed to allocate setup buffer\n"); ++ ret = -ENOMEM; ++ goto err2; ++ } ++ ++ dwc->ep0_bounce = dma_alloc_coherent(dwc->dev, ++ 512, &dwc->ep0_bounce_addr, GFP_KERNEL); ++ if (!dwc->ep0_bounce) { ++ dev_err(dwc->dev, "failed to allocate ep0 bounce buffer\n"); ++ ret = -ENOMEM; ++ goto err3; ++ } ++ ++ dev_set_name(&dwc->gadget.dev, "gadget"); ++ ++ dwc->gadget.ops = &dwc3_gadget_ops; ++ dwc->gadget.is_dualspeed = true; ++ dwc->gadget.speed = USB_SPEED_UNKNOWN; ++ ++ dwc->gadget.name = "dwc3-gadget"; ++ ++ the_dwc = dwc; ++ ++ /* ++ * REVISIT: Here we should clear all pending IRQs to be ++ * sure we're starting from a well known location. ++ */ ++ ++ ret = dwc3_gadget_init_endpoints(dwc); ++ if (ret) ++ goto err4; ++ ++ /* Enable all but Start and End of Frame IRQs */ ++ reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN | ++ DWC3_DEVTEN_EVNTOVERFLOWEN | ++ DWC3_DEVTEN_CMDCMPLTEN | ++ DWC3_DEVTEN_ERRTICERREN | ++ DWC3_DEVTEN_WKUPEVTEN | ++ DWC3_DEVTEN_ULSTCNGEN | ++ DWC3_DEVTEN_CONNECTDONEEN | ++ DWC3_DEVTEN_USBRSTEN | ++ DWC3_DEVTEN_DISCONNEVTEN); ++ dwc3_writel(dwc->regs, DWC3_DEVTEN, reg); ++ return 0; ++ ++ device_unregister(&dwc->gadget.dev); ++ dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00); ++ dwc3_gadget_free_endpoints(dwc); ++ ++err4: ++ dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce, ++ dwc->ep0_bounce_addr); ++ ++err3: ++ dma_free_coherent(dwc->dev, sizeof(*dwc->setup_buf) * 2, ++ dwc->setup_buf, dwc->setup_buf_addr); ++ ++err2: ++ dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb), ++ dwc->ep0_trb, dwc->ep0_trb_addr); ++ ++err1: ++ dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req), ++ dwc->ctrl_req, dwc->ctrl_req_addr); ++ ++err0: ++ return ret; ++} ++ ++void dwc3_gadget_exit(struct dwc3 *dwc) ++{ ++ dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00); ++ ++ dwc3_gadget_free_endpoints(dwc); ++ ++ dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce, ++ dwc->ep0_bounce_addr); ++ ++ dma_free_coherent(dwc->dev, sizeof(*dwc->setup_buf) * 2, ++ dwc->setup_buf, dwc->setup_buf_addr); ++ ++ dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb), ++ dwc->ep0_trb, dwc->ep0_trb_addr); ++ ++ dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req), ++ dwc->ctrl_req, dwc->ctrl_req_addr); ++ ++ device_unregister(&dwc->gadget.dev); ++ ++ the_dwc = NULL; ++} ++ ++/* -------------------------------------------------------------------------- */ ++ ++int usb_gadget_register_driver(struct usb_gadget_driver *driver) ++{ ++ return usb_gadget_probe_driver(driver, driver->bind); ++} ++ ++/** ++ * usb_gadget_unregister_driver - unregisters a gadget driver. ++ * @driver: the gadget driver to unregister ++ */ ++int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) ++{ ++ struct dwc3 *dwc = the_dwc; ++ unsigned long flags; ++ ++ if (!driver || !driver->unbind) ++ return -EINVAL; ++ ++ if (!dwc) ++ return -ENODEV; ++ ++ if (dwc->gadget_driver != driver) ++ return -EINVAL; ++ ++ driver->disconnect(&dwc->gadget); ++ driver->unbind(&dwc->gadget); ++ ++ spin_lock_irqsave(&dwc->lock, flags); ++ ++ dwc->gadget_driver = NULL; ++ ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ ++ return 0; ++} +diff --git a/drivers/usb/dwc3/dwc3_host.c b/drivers/usb/dwc3/dwc3_host.c +new file mode 100644 +index 0000000..7cfe211 +--- /dev/null ++++ b/drivers/usb/dwc3/dwc3_host.c +@@ -0,0 +1,102 @@ ++/** ++ * host.c - DesignWare USB3 DRD Controller Host Glue ++ * ++ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com ++ * ++ * Authors: Felipe Balbi , ++ * ++ * 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 the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not 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. ++ * ++ * 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 MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. ++ */ ++ ++#include ++ ++#include "core.h" ++ ++static struct resource generic_resources[] = { ++ { ++ .flags = IORESOURCE_IRQ, ++ }, ++ { ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++int dwc3_host_init(struct dwc3 *dwc) ++{ ++ struct platform_device *xhci; ++ int ret; ++ ++ xhci = platform_device_alloc("xhci", -1); ++ if (!xhci) { ++ dev_err(dwc->dev, "couldn't allocate xHCI device\n"); ++ ret = -ENOMEM; ++ goto err0; ++ } ++ ++ dma_set_coherent_mask(&xhci->dev, dwc->dev->coherent_dma_mask); ++ ++ xhci->dev.parent = dwc->dev; ++ xhci->dev.dma_mask = dwc->dev->dma_mask; ++ xhci->dev.dma_parms = dwc->dev->dma_parms; ++ ++ dwc->xhci = xhci; ++ ++ /* setup resources */ ++ generic_resources[0].start = dwc->irq; ++ ++ generic_resources[1].start = dwc->res->start; ++ generic_resources[1].end = dwc->res->start + 0x7fff; ++ ++ ret = platform_device_add_resources(xhci, generic_resources, ++ ARRAY_SIZE(generic_resources)); ++ if (ret) { ++ dev_err(dwc->dev, "couldn't add resources to xHCI device\n"); ++ goto err1; ++ } ++ ++ ret = platform_device_add(xhci); ++ if (ret) { ++ dev_err(dwc->dev, "failed to register xHCI device\n"); ++ goto err1; ++ } ++ ++ return 0; ++ ++err1: ++ platform_device_put(xhci); ++ ++err0: ++ return ret; ++} ++ ++void dwc3_host_exit(struct dwc3 *dwc) ++{ ++ platform_device_unregister(dwc->xhci); ++} +diff --git a/drivers/usb/dwc3/dwc3_misc.c b/drivers/usb/dwc3/dwc3_misc.c +new file mode 100644 +index 0000000..628e11e +--- /dev/null ++++ b/drivers/usb/dwc3/dwc3_misc.c +@@ -0,0 +1,20 @@ ++#include ++ ++#include ++#include ++ ++#include "misc.h" ++#if 0 ++int snprintf(char *buf, size_t size, const char *fmt, ...) ++{ ++ va_list args; ++ int i; ++ ++ va_start(args, fmt); ++ i = vsprintf(buf, fmt, args); ++ va_end(args); ++ if (i > size) ++ printf("*** Wrote too much bytes into the buffer ***\n"); ++ return i; ++} ++#endif +diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h +new file mode 100644 +index 0000000..d97f467 +--- /dev/null ++++ b/drivers/usb/dwc3/gadget.h +@@ -0,0 +1,186 @@ ++/** ++ * gadget.h - DesignWare USB3 DRD Gadget Header ++ * ++ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com ++ * ++ * Authors: Felipe Balbi , ++ * Sebastian Andrzej Siewior ++ * ++ * 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 the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not 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. ++ * ++ * 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 MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. ++ */ ++ ++#ifndef __DRIVERS_USB_DWC3_GADGET_H ++#define __DRIVERS_USB_DWC3_GADGET_H ++ ++#include ++#include ++#include "io.h" ++ ++struct dwc3; ++#define to_dwc3_ep(ep) (container_of(ep, struct dwc3_ep, endpoint)) ++#define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget)) ++ ++/* DEPCFG parameter 1 */ ++#define DWC3_DEPCFG_INT_NUM(n) ((n) << 0) ++#define DWC3_DEPCFG_XFER_COMPLETE_EN (1 << 8) ++#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN (1 << 9) ++#define DWC3_DEPCFG_XFER_NOT_READY_EN (1 << 10) ++#define DWC3_DEPCFG_FIFO_ERROR_EN (1 << 11) ++#define DWC3_DEPCFG_STREAM_EVENT_EN (1 << 13) ++#define DWC3_DEPCFG_BINTERVAL_M1(n) ((n) << 16) ++#define DWC3_DEPCFG_STREAM_CAPABLE (1 << 24) ++#define DWC3_DEPCFG_EP_NUMBER(n) ((n) << 25) ++#define DWC3_DEPCFG_BULK_BASED (1 << 30) ++#define DWC3_DEPCFG_FIFO_BASED (1 << 31) ++ ++/* DEPCFG parameter 0 */ ++#define DWC3_DEPCFG_EP_TYPE(n) ((n) << 1) ++#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) ((n) << 3) ++#define DWC3_DEPCFG_FIFO_NUMBER(n) ((n) << 17) ++#define DWC3_DEPCFG_BURST_SIZE(n) ((n) << 22) ++#define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26) ++#define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31) ++ ++/* DEPXFERCFG parameter 0 */ ++#define DWC3_DEPXFERCFG_NUM_XFER_RES(n) ((n) & 0xffff) ++ ++struct dwc3_gadget_ep_cmd_params { ++ u32 param2; ++ u32 param1; ++ u32 param0; ++}; ++ ++/* -------------------------------------------------------------------------- */ ++ ++#define to_dwc3_request(r) (container_of(r, struct dwc3_request, request)) ++ ++static inline struct dwc3_request *next_request(struct list_head *list) ++{ ++ if (list_empty(list)) ++ return NULL; ++ ++ return list_first_entry(list, struct dwc3_request, list); ++} ++ ++static inline void dwc3_gadget_move_request_queued(struct dwc3_request *req) ++{ ++ struct dwc3_ep *dep = req->dep; ++ ++ req->queued = true; ++ list_move_tail(&req->list, &dep->req_queued); ++} ++ ++void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, ++ int status); ++ ++void dwc3_ep0_interrupt(struct dwc3 *dwc, ++ const struct dwc3_event_depevt *event); ++void dwc3_ep0_out_start(struct dwc3 *dwc); ++int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, ++ gfp_t gfp_flags); ++int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value); ++int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, ++ unsigned cmd, struct dwc3_gadget_ep_cmd_params *params); ++void dwc3_map_buffer_to_dma(struct dwc3_request *req); ++void dwc3_unmap_buffer_from_dma(struct dwc3_request *req); ++ ++/** ++ * dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW ++ * @dwc: DesignWare USB3 Pointer ++ * @number: DWC endpoint number ++ * ++ * Caller should take care of locking ++ */ ++static inline u32 dwc3_gadget_ep_get_transfer_index(struct dwc3 *dwc, u8 number) ++{ ++ u32 res_id; ++ ++ res_id = dwc3_readl(dwc->regs, DWC3_DEPCMD(number)); ++ ++ return DWC3_DEPCMD_GET_RSC_IDX(res_id); ++} ++ ++/** ++ * dwc3_gadget_event_string - returns event name ++ * @event: the event code ++ */ ++static inline const char *dwc3_gadget_event_string(u8 event) ++{ ++ switch (event) { ++ case DWC3_DEVICE_EVENT_DISCONNECT: ++ return "Disconnect"; ++ case DWC3_DEVICE_EVENT_RESET: ++ return "Reset"; ++ case DWC3_DEVICE_EVENT_CONNECT_DONE: ++ return "Connection Done"; ++ case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: ++ return "Link Status Change"; ++ case DWC3_DEVICE_EVENT_WAKEUP: ++ return "WakeUp"; ++ case DWC3_DEVICE_EVENT_EOPF: ++ return "End-Of-Frame"; ++ case DWC3_DEVICE_EVENT_SOF: ++ return "Start-Of-Frame"; ++ case DWC3_DEVICE_EVENT_ERRATIC_ERROR: ++ return "Erratic Error"; ++ case DWC3_DEVICE_EVENT_CMD_CMPL: ++ return "Command Complete"; ++ case DWC3_DEVICE_EVENT_OVERFLOW: ++ return "Overflow"; ++ } ++ ++ return "UNKNOWN"; ++} ++ ++/** ++ * dwc3_ep_event_string - returns event name ++ * @event: then event code ++ */ ++static inline const char *dwc3_ep_event_string(u8 event) ++{ ++ switch (event) { ++ case DWC3_DEPEVT_XFERCOMPLETE: ++ return "Transfer Complete"; ++ case DWC3_DEPEVT_XFERINPROGRESS: ++ return "Transfer In-Progress"; ++ case DWC3_DEPEVT_XFERNOTREADY: ++ return "Transfer Not Ready"; ++ case DWC3_DEPEVT_RXTXFIFOEVT: ++ return "FIFO"; ++ case DWC3_DEPEVT_STREAMEVT: ++ return "Stream"; ++ case DWC3_DEPEVT_EPCMDCMPLT: ++ return "Endpoint Command Complete"; ++ } ++ ++ return "UNKNOWN"; ++} ++ ++#endif /* __DRIVERS_USB_DWC3_GADGET_H */ +diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h +new file mode 100644 +index 0000000..bc957db +--- /dev/null ++++ b/drivers/usb/dwc3/io.h +@@ -0,0 +1,54 @@ ++/** ++ * io.h - DesignWare USB3 DRD IO Header ++ * ++ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com ++ * ++ * Authors: Felipe Balbi , ++ * Sebastian Andrzej Siewior ++ * ++ * 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 the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not 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. ++ * ++ * 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 MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. ++ */ ++ ++#ifndef __DRIVERS_USB_DWC3_IO_H ++#define __DRIVERS_USB_DWC3_IO_H ++ ++#include ++ ++static inline u32 dwc3_readl(void __iomem *base, u32 offset) ++{ ++ return readl(base + offset); ++} ++ ++static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value) ++{ ++ writel(value, base + offset); ++} ++ ++#endif /* __DRIVERS_USB_DWC3_IO_H */ +diff --git a/drivers/usb/dwc3/misc.h b/drivers/usb/dwc3/misc.h +new file mode 100644 +index 0000000..da9012c +--- /dev/null ++++ b/drivers/usb/dwc3/misc.h +@@ -0,0 +1,269 @@ ++#ifndef dwc3_misc_h ++#define dwc3_misc_h ++ ++#include ++#include ++#include ++#include ++ ++#define DWC3_USB_REGS_SIZE (CONFIG_USB_DWC3_UDC_REGS - \ ++ CONFIG_USB_DWC3_UDC_REGS_END + 1) ++#define DWC3_WRAPPER_REGS_SIZE (CONFIG_USB_DWC3_WRAP_REGS - \ ++ CONFIG_USB_DWC3_WRAP_REGS_END + 1) ++ ++#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16)) ++#define lower_32_bits(n) ((u32)(n)) ++ ++extern struct dwc3 *global_dwc3; ++ ++typedef int mutex_t; ++typedef int spinlock_t; ++/* ++enum { ++ false = 0, ++ true = 1 ++}; ++typedef unsigned int bool; ++*/ ++#define min_t(type, x, y) ({ \ ++ type __min1 = (x); \ ++ type __min2 = (y); \ ++ __min1 < __min2 ? __min1: __min2; }) ++ ++#define __init ++#define __devinit ++#define __devinitconst ++#define __devexit ++ ++static inline void *dma_alloc_coherent(void *dev, size_t size, ++ dma_addr_t *dma_handle, gfp_t gfp) ++{ ++ void *p; ++ ++ p = malloc(size); ++ *dma_handle = (unsigned long)p; ++ return p; ++} ++ ++ ++static inline void dma_free_coherent(struct device *dev, size_t size, ++ void *vaddr, dma_addr_t bus) ++{ ++ free(vaddr); ++} ++ ++static inline void kfree(void *p) ++{ ++ free(p); ++} ++ ++static inline void *kzalloc(unsigned int size, unsigned int flags) ++{ ++ void *p; ++ ++ p = malloc(size); ++ memset(p, 0, size); ++ return p; ++} ++ ++#define GFP_KERNEL 0 ++ ++#define MAX_ERRNO 4095 ++ ++#define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO) ++ ++static inline void *ERR_PTR(long error) ++{ ++ return (void *) error; ++} ++ ++static inline long PTR_ERR(const void *ptr) ++{ ++ return (long) ptr; ++} ++ ++static inline long IS_ERR(const void *ptr) ++{ ++ return IS_ERR_VALUE((unsigned long)ptr); ++} ++ ++#define dev_err(dev, format, ...) printf(format, ## __VA_ARGS__) ++#if 0 ++#define dev_dbg(dev, format, ...) printf(format, ## __VA_ARGS__) ++#define dev_vdbg(dev, format, ...) printf(format, ## __VA_ARGS__) ++#define pr_debug(format, ...) printf(format, ## __VA_ARGS__) ++#else ++static inline void dwc3_valength_dummy(void *p, char *fmt, ...) {} ++#define dev_dbg(dev, format, ...) dwc3_valength_dummy(dev, format, ## __VA_ARGS__) ++#define dev_vdbg(dev, format, ...) dwc3_valength_dummy(dev, format, ## __VA_ARGS__) ++#define pr_debug(format, ...) dwc3_valength_dummy(NULL, format, ## __VA_ARGS__) ++#endif ++ ++static inline void pm_runtime_enable(struct device *dev) {} ++static inline void pm_runtime_get_sync(struct device *dev) {} ++static inline void pm_runtime_forbid(struct device *dev) {} ++static inline void pm_runtime_allow(struct device *dev) {} ++ ++#define PTR_ALIGN(p, a) ((typeof(p))ALIGN((unsigned long)(p), (a))) ++ ++#define PAGE_SHIFT 12 ++#define PAGE_SIZE (1UL << PAGE_SHIFT) ++#define PAGE_MASK (~(PAGE_SIZE-1)) ++ ++struct platform_device { ++ struct device dev; ++}; ++ ++static inline void __arch_iounmap(void *p) {} ++ ++#define cpu_relax() asm volatile("" ::: "memory") ++ ++enum dma_data_direction { ++ DMA_BIDIRECTIONAL = 0, ++ DMA_TO_DEVICE = 1, ++ DMA_FROM_DEVICE = 2, ++}; ++ ++static inline dma_addr_t dma_map_single(struct device *dev, void *ptr, ++ size_t size, ++ enum dma_data_direction dir) ++{ ++ return (unsigned long)ptr; ++} ++ ++static inline void dma_unmap_single(struct device *dev, dma_addr_t addr, ++ size_t size, ++ enum dma_data_direction dir) ++{ ++} ++ ++static inline void dma_sync_single_for_device(struct device *dev, ++ dma_addr_t addr, size_t size, ++ enum dma_data_direction dir) ++{ ++} ++static inline void dma_sync_single_for_cpu(struct device *dev, dma_addr_t addr, ++ size_t size, ++ enum dma_data_direction dir) ++{ ++} ++ ++static inline int device_register(struct device *dev) ++{ ++ return 0; ++} ++ ++static inline void device_unregister(struct device *dev) ++{ ++} ++ ++static inline int dev_set_name(struct device *dev, const char *fmt, ...) ++{ ++ return 0; ++} ++ ++#define spin_lock(x) ++#define spin_unlock(x) ++#define spin_lock_irqsave(x, y) y = 0; ++#define spin_unlock_irqrestore(x, y) ++#if 0 ++static inline void mdelay(unsigned int msec) ++{ ++ int i; ++ ++ /* XXX VirtIO currently hangs on udelay(10+) */ ++ for (i = 0; i < msec; i++) ++ udelay(5); ++} ++#endif ++static inline void msleep(unsigned int msec) ++{ ++ mdelay(msec); ++} ++ ++static inline void usleep_range(unsigned long min, unsigned long max) ++{ ++ udelay(min); ++} ++ ++#define container_of(ptr, type, member) ({ \ ++ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ ++ (type *)( (char *)__mptr - offsetof(type,member) );}) ++ ++#define __WARN_printf(arg...) do { printf("WarnON() in %s: %s(%d)\n", __FILE__, __func__, __LINE__); printf(arg); } while (0) ++ ++#define WARN(condition, format...) ({ \ ++ int __ret_warn_on = !!(condition); \ ++ if (unlikely(__ret_warn_on)) \ ++ __WARN_printf(format); \ ++ unlikely(__ret_warn_on); \ ++ }) ++ ++#define WARN_ONCE(condition, format...) ({ \ ++ static bool __warned; \ ++ int __ret_warn_once = !!(condition); \ ++ \ ++ if (unlikely(__ret_warn_once)) \ ++ if (WARN(!__warned, format)) \ ++ __warned = true; \ ++ unlikely(__ret_warn_once); \ ++ }) ++ ++#define dev_WARN_ONCE(dev, condition, format, arg...) \ ++ WARN_ONCE(condition, format, ## arg) ++ ++#define WARN_ON(condition) ({ \ ++ int __ret_warn_on = !!(condition); \ ++ if (unlikely(__ret_warn_on)) \ ++ printf("WarnON() in %s: %s(%d)\n", __FILE__, __func__, __LINE__); \ ++ unlikely(__ret_warn_on); \ ++ }) ++ ++#define WARN_ON_ONCE(condition) ({ \ ++ static bool __warned; \ ++ int __ret_warn_once = !!(condition); \ ++ \ ++ if (unlikely(__ret_warn_once)) \ ++ if (WARN_ON(!__warned)) \ ++ __warned = true; \ ++ unlikely(__ret_warn_once); \ ++ }) ++ ++#define BUILD_BUG_ON_NOT_POWER_OF_2(n) \ ++ BUILD_BUG_ON((n) == 0 || (((n) & ((n) - 1)) != 0)) ++#if 0 ++/* never do isoc */ ++static inline int usb_endpoint_xfer_isoc( ++ const struct usb_endpoint_descriptor *epd) ++{ ++ return 0; ++} ++static inline int usb_endpoint_type(const struct usb_endpoint_descriptor *epd) ++{ ++ return epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; ++} ++#endif ++ ++enum irqreturn { ++ IRQ_NONE, ++ IRQ_HANDLED, ++ IRQ_WAKE_THREAD, ++}; ++ ++typedef enum irqreturn irqreturn_t; ++ ++int usb_gadget_probe_driver(struct usb_gadget_driver *driver, ++ int (*bind)(struct usb_gadget *)); ++int usb_gadget_unregister_driver(struct usb_gadget_driver *driver); ++ ++struct dwc3; ++int snprintf(char *buf, size_t size, const char *fmt, ...); ++int __devinit dwc3_probe(struct platform_device *pdev); ++int __devexit dwc3_remove(struct platform_device *pdev); ++ ++extern unsigned int dwc3_cable_connected; ++extern unsigned int dwc3_high_speed; ++ ++#define USB_GADGET_DELAYED_STATUS 0x7fff /* Impossibly large value */ ++ ++#endif +diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile +index 804a2bd..1ada7bb 100644 +--- a/drivers/usb/gadget/Makefile ++++ b/drivers/usb/gadget/Makefile +@@ -17,6 +17,7 @@ obj-$(CONFIG_THOR_FUNCTION) += f_thor.o + obj-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o + obj-$(CONFIG_DFU_FUNCTION) += f_dfu.o + obj-$(CONFIG_USB_GADGET_MASS_STORAGE) += f_mass_storage.o ++obj-$(CONFIG_USB_FASTBOOT) += f_fastboot.o u_fastboot.o + endif + ifdef CONFIG_USB_ETHER + obj-y += ether.o +diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c +index de75ff1..3b8b48b 100644 +--- a/drivers/usb/gadget/f_dfu.c ++++ b/drivers/usb/gadget/f_dfu.c +@@ -601,6 +601,7 @@ dfu_handle(struct usb_function *f, const struct usb_ctrlrequest *ctrl) + (w_value >> 8) == DFU_DT_FUNC) { + value = min(len, (u16) sizeof(dfu_func)); + memcpy(req->buf, &dfu_func, value); ++ dfu_trigger_enum_done(); + } + } else /* DFU specific request */ + value = dfu_state[f_dfu->dfu_state] (f_dfu, ctrl, gadget, req); +diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c +new file mode 100644 +index 0000000..7570440 +--- /dev/null ++++ b/drivers/usb/gadget/f_fastboot.c +@@ -0,0 +1,559 @@ ++/* ++ * (C) Copyright 2008 - 2009 ++ * Windriver, ++ * Tom Rix ++ * ++ * Copyright (c) 2011 Sebastian Andrzej Siewior ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of ++ * the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, ++ * MA 02111-1307 USA ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "g_fastboot.h" ++ ++#define CONFIGURATION_NORMAL 1 ++#define BULK_ENDPOINT 1 ++#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0 (0x0200) ++#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1 (0x0040) ++#define TX_ENDPOINT_MAXIMUM_PACKET_SIZE (0x0040) ++ ++static struct usb_string def_usb_fb_strings[] = { ++ { FB_STR_PRODUCT_IDX, "Default Product" }, ++ { FB_STR_SERIAL_IDX, "1234567890" }, ++ { FB_STR_CONFIG_IDX, "Android Fastboot" }, ++ { FB_STR_INTERFACE_IDX, "Android Fastboot" }, ++ { FB_STR_MANUFACTURER_IDX, "Default Manufacturer" }, ++ { FB_STR_PROC_REV_IDX, "Default 1.0" }, ++ { FB_STR_PROC_TYPE_IDX, "Emulator" }, ++ { } ++}; ++ ++static struct usb_gadget_strings def_fb_strings = { ++ .language = 0x0409, /* en-us */ ++ .strings = def_usb_fb_strings, ++}; ++ ++static struct usb_gadget_strings *vendor_fb_strings; ++ ++static unsigned int gadget_is_connected; ++ ++static u8 ep0_buffer[512]; ++static u8 ep_out_buffer[EP_BUFFER_SIZE]; ++static u8 ep_in_buffer[EP_BUFFER_SIZE]; ++static int current_config; ++ ++/* e1 */ ++static struct usb_endpoint_descriptor fs_ep_in = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, /* IN */ ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = TX_ENDPOINT_MAXIMUM_PACKET_SIZE, ++ .bInterval = 0x00, ++}; ++ ++/* e2 */ ++static struct usb_endpoint_descriptor fs_ep_out = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_OUT, /* OUT */ ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1, ++ .bInterval = 0x00, ++}; ++ ++static struct usb_endpoint_descriptor hs_ep_out = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_OUT, /* OUT */ ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0, ++ .bInterval = 0x00, ++}; ++ ++const char *fb_find_usb_string(unsigned int id) ++{ ++ struct usb_string *s; ++ ++ for (s = vendor_fb_strings->strings; s && s->s; s++) { ++ if (s->id == id) ++ break; ++ } ++ if (!s || !s->s) { ++ for (s = def_fb_strings.strings; s && s->s; s++) { ++ if (s->id == id) ++ break; ++ } ++ } ++ if (!s) ++ return NULL; ++ return s->s; ++} ++ ++static struct usb_gadget *g; ++static struct usb_request *ep0_req; ++ ++struct usb_ep *ep_in; ++struct usb_request *req_in; ++ ++struct usb_ep *ep_out; ++struct usb_request *req_out; ++ ++static void fastboot_ep0_complete(struct usb_ep *ep, struct usb_request *req) ++{ ++ int status = req->status; ++ ++ if (!status) ++ return; ++ printf("ep0 status %d\n", status); ++} ++ ++static int fastboot_bind(struct usb_gadget *gadget) ++{ ++ ++ g = gadget; ++ ep0_req = usb_ep_alloc_request(g->ep0, 0); ++ if (!ep0_req) ++ goto err; ++ ep0_req->buf = ep0_buffer; ++ ep0_req->complete = fastboot_ep0_complete; ++ ++ ep_in = usb_ep_autoconfig(gadget, &fs_ep_in); ++ if (!ep_in) ++ goto err; ++ ep_in->driver_data = ep_in; ++ ++ ep_out = usb_ep_autoconfig(gadget, &fs_ep_out); ++ if (!ep_out) ++ goto err; ++ ep_out->driver_data = ep_out; ++ ++ hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress; ++ return 0; ++err: ++ return -1; ++} ++ ++static void fastboot_unbind(struct usb_gadget *gadget) ++{ ++ usb_ep_free_request(g->ep0, ep0_req); ++ ep_in->driver_data = NULL; ++ ep_out->driver_data = NULL; ++} ++ ++/* This is the TI USB vendor id a product ID from TI's internal tree */ ++#define DEVICE_VENDOR_ID 0x0451 ++#define DEVICE_PRODUCT_ID 0xd022 ++#define DEVICE_BCD 0x0100 ++ ++struct usb_device_descriptor fb_descriptor = { ++ .bLength = sizeof(fb_descriptor), ++ .bDescriptorType = USB_DT_DEVICE, ++ .bcdUSB = 0x200, ++ .bMaxPacketSize0 = 0x40, ++ .idVendor = DEVICE_VENDOR_ID, ++ .idProduct = DEVICE_PRODUCT_ID, ++ .bcdDevice = DEVICE_BCD, ++ .iManufacturer = FB_STR_MANUFACTURER_IDX, ++ .iProduct = FB_STR_PRODUCT_IDX, ++ .iSerialNumber = FB_STR_SERIAL_IDX, ++ .bNumConfigurations = 1, ++}; ++ ++#define TOT_CFG_DESC_LEN (USB_DT_CONFIG_SIZE + USB_DT_INTERFACE_SIZE + \ ++ USB_DT_ENDPOINT_SIZE + USB_DT_ENDPOINT_SIZE) ++ ++static struct usb_config_descriptor config_desc = { ++ .bLength = USB_DT_CONFIG_SIZE, ++ .bDescriptorType = USB_DT_CONFIG, ++ .wTotalLength = cpu_to_le16(TOT_CFG_DESC_LEN), ++ .bNumInterfaces = 1, ++ .bConfigurationValue = CONFIGURATION_NORMAL, ++ .iConfiguration = FB_STR_CONFIG_IDX, ++ .bmAttributes = 0xc0, ++ .bMaxPower = 0x32, ++}; ++ ++static struct usb_interface_descriptor interface_desc = { ++ .bLength = USB_DT_INTERFACE_SIZE, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bInterfaceNumber = 0x00, ++ .bAlternateSetting = 0x00, ++ .bNumEndpoints = 0x02, ++ .bInterfaceClass = FASTBOOT_INTERFACE_CLASS, ++ .bInterfaceSubClass = FASTBOOT_INTERFACE_SUB_CLASS, ++ .bInterfaceProtocol = FASTBOOT_INTERFACE_PROTOCOL, ++ .iInterface = FB_STR_INTERFACE_IDX, ++}; ++ ++static struct usb_qualifier_descriptor qual_desc = { ++ .bLength = sizeof(qual_desc), ++ .bDescriptorType = USB_DT_DEVICE_QUALIFIER, ++ .bcdUSB = 0x200, ++ .bMaxPacketSize0 = 0x40, ++ .bNumConfigurations = 1, ++}; ++ ++static int fastboot_setup_get_descr(struct usb_gadget *gadget, ++ const struct usb_ctrlrequest *ctrl) ++{ ++ u16 w_value = le16_to_cpu(ctrl->wValue); ++ u16 w_length = le16_to_cpu(ctrl->wLength); ++ u16 val; ++ int ret; ++ u32 bytes_remaining; ++ u32 bytes_total; ++ u32 this_inc; ++ ++ val = w_value >> 8; ++ ++ switch (val) { ++ case USB_DT_DEVICE: ++ ++ memcpy(ep0_buffer, &fb_descriptor, sizeof(fb_descriptor)); ++ ep0_req->length = min(w_length, sizeof(fb_descriptor)); ++ ret = usb_ep_queue(gadget->ep0, ep0_req, 0); ++ break; ++ ++ case USB_DT_CONFIG: ++ ++ bytes_remaining = min(w_length, sizeof(ep0_buffer)); ++ bytes_total = 0; ++ ++ /* config */ ++ this_inc = min(bytes_remaining, USB_DT_CONFIG_SIZE); ++ bytes_remaining -= this_inc; ++ memcpy(ep0_buffer + bytes_total, &config_desc, this_inc); ++ bytes_total += this_inc; ++ ++ /* interface */ ++ this_inc = min(bytes_remaining, USB_DT_INTERFACE_SIZE); ++ bytes_remaining -= this_inc; ++ memcpy(ep0_buffer + bytes_total, &interface_desc, this_inc); ++ bytes_total += this_inc; ++ ++ /* ep in */ ++ this_inc = min(bytes_remaining, USB_DT_ENDPOINT_SIZE); ++ bytes_remaining -= this_inc; ++ memcpy(ep0_buffer + bytes_total, &fs_ep_in, this_inc); ++ bytes_total += this_inc; ++ ++ /* ep out */ ++ this_inc = min(bytes_remaining, USB_DT_ENDPOINT_SIZE); ++ ++ if (gadget->speed == USB_SPEED_HIGH) ++ memcpy(ep0_buffer + bytes_total, &hs_ep_out, ++ this_inc); ++ else ++ memcpy(ep0_buffer + bytes_total, &fs_ep_out, ++ this_inc); ++ bytes_total += this_inc; ++ ++ ep0_req->length = bytes_total; ++ ret = usb_ep_queue(gadget->ep0, ep0_req, 0); ++ break; ++ ++ case USB_DT_STRING: ++ ++ ret = usb_gadget_get_string(vendor_fb_strings, ++ w_value & 0xff, ep0_buffer); ++ if (ret < 0) ++ ret = usb_gadget_get_string(&def_fb_strings, ++ w_value & 0xff, ep0_buffer); ++ if (ret < 0) ++ break; ++ ++ ep0_req->length = ret; ++ ret = usb_ep_queue(gadget->ep0, ep0_req, 0); ++ break; ++ ++ case USB_DT_DEVICE_QUALIFIER: ++ ++ memcpy(ep0_buffer, &qual_desc, sizeof(qual_desc)); ++ ep0_req->length = min(w_length, sizeof(qual_desc)); ++ ret = usb_ep_queue(gadget->ep0, ep0_req, 0); ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ return ret; ++} ++ ++static int fastboot_setup_get_conf(struct usb_gadget *gadget, ++ const struct usb_ctrlrequest *ctrl) ++{ ++ u16 w_length = le16_to_cpu(ctrl->wLength); ++ ++ if (w_length == 0) ++ return -1; ++ ++ ep0_buffer[0] = current_config; ++ ep0_req->length = 1; ++ return usb_ep_queue(gadget->ep0, ep0_req, 0); ++} ++ ++static void fastboot_complete_in(struct usb_ep *ep, struct usb_request *req) ++{ ++ int status = req->status; ++ ++ if (status) ++ printf("status: %d ep_in trans: %d\n", ++ status, ++ req->actual); ++} ++ ++static int fastboot_disable_ep(struct usb_gadget *gadget) ++{ ++ if (req_out) { ++ usb_ep_free_request(ep_out, req_out); ++ req_out = NULL; ++ } ++ if (req_in) { ++ usb_ep_free_request(ep_in, req_in); ++ req_in = NULL; ++ } ++ usb_ep_disable(ep_out); ++ usb_ep_disable(ep_in); ++ ++ return 0; ++} ++ ++static int fastboot_enable_ep(struct usb_gadget *gadget) ++{ ++ int ret; ++ ++ /* make sure we don't enable the ep twice */ ++ if (gadget->speed == USB_SPEED_HIGH) ++ ret = usb_ep_enable(ep_out, &hs_ep_out); ++ else ++ ret = usb_ep_enable(ep_out, &fs_ep_out); ++ if (ret) { ++ printf("failed to enable out ep\n"); ++ goto err; ++ } ++ ++ req_out = usb_ep_alloc_request(ep_out, 0); ++ if (!req_out) { ++ printf("failed to alloc out req\n"); ++ goto err; ++ } ++ ++ ret = usb_ep_enable(ep_in, &fs_ep_in); ++ if (ret) { ++ printf("failed to enable in ep\n"); ++ goto err; ++ } ++ req_in = usb_ep_alloc_request(ep_in, 0); ++ if (!req_in) { ++ printf("failed alloc req in\n"); ++ goto err; ++ } ++ ++ req_out->complete = rx_handler_command; ++ req_out->buf = ep_out_buffer; ++ req_out->length = sizeof(ep_out_buffer); ++ ++ req_in->buf = ep_in_buffer; ++ req_in->length = sizeof(ep_in_buffer); ++ ++ ret = usb_ep_queue(ep_out, req_out, 0); ++ if (ret) ++ goto err; ++ ++ return 0; ++err: ++ fastboot_disable_ep(gadget); ++ return -1; ++} ++ ++static int fastboot_set_interface(struct usb_gadget *gadget, u32 enable) ++{ ++ if (enable && req_out) ++ return 0; ++ if (!enable && !req_out) ++ return 0; ++ ++ if (enable) ++ return fastboot_enable_ep(gadget); ++ else ++ return fastboot_disable_ep(gadget); ++} ++ ++static int fastboot_setup_out_req(struct usb_gadget *gadget, ++ const struct usb_ctrlrequest *req) ++{ ++ switch (req->bRequestType & USB_RECIP_MASK) { ++ case USB_RECIP_DEVICE: ++ switch (req->bRequest) { ++ case USB_REQ_SET_CONFIGURATION: ++ ++ ep0_req->length = 0; ++ if (req->wValue == CONFIGURATION_NORMAL) { ++ current_config = CONFIGURATION_NORMAL; ++ fastboot_set_interface(gadget, 1); ++ return usb_ep_queue(gadget->ep0, ++ ep0_req, 0); ++ } ++ if (req->wValue == 0) { ++ current_config = 0; ++ fastboot_set_interface(gadget, 0); ++ return usb_ep_queue(gadget->ep0, ++ ep0_req, 0); ++ } ++ return -1; ++ break; ++ default: ++ return -1; ++ }; ++ ++ case USB_RECIP_INTERFACE: ++ switch (req->bRequest) { ++ case USB_REQ_SET_INTERFACE: ++ ++ ep0_req->length = 0; ++ if (!fastboot_set_interface(gadget, 1)) ++ return usb_ep_queue(gadget->ep0, ++ ep0_req, 0); ++ return -1; ++ break; ++ default: ++ return -1; ++ } ++ ++ case USB_RECIP_ENDPOINT: ++ switch (req->bRequest) { ++ case USB_REQ_CLEAR_FEATURE: ++ ++ return usb_ep_queue(gadget->ep0, ep0_req, 0); ++ break; ++ default: ++ return -1; ++ } ++ } ++ return -1; ++} ++ ++static int fastboot_setup(struct usb_gadget *gadget, ++ const struct usb_ctrlrequest *req) ++{ ++ if ((req->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) ++ return -1; ++ ++ if ((req->bRequestType & USB_DIR_IN) == 0) ++ /* host-to-device */ ++ return fastboot_setup_out_req(gadget, req); ++ ++ /* device-to-host */ ++ if ((req->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) { ++ switch (req->bRequest) { ++ case USB_REQ_GET_DESCRIPTOR: ++ return fastboot_setup_get_descr(gadget, req); ++ break; ++ ++ case USB_REQ_GET_CONFIGURATION: ++ return fastboot_setup_get_conf(gadget, req); ++ break; ++ default: ++ return -1; ++ } ++ } ++ return -1; ++} ++ ++static void fastboot_disconnect(struct usb_gadget *gadget) ++{ ++ fastboot_disable_ep(gadget); ++ gadget_is_connected = 0; ++} ++ ++struct usb_gadget_driver fast_gadget = { ++ .bind = fastboot_bind, ++ .unbind = fastboot_unbind, ++ .setup = fastboot_setup, ++ .disconnect = fastboot_disconnect, ++}; ++ ++static int udc_is_probbed; ++ ++int fastboot_init(void) ++{ ++ int ret; ++ ++ ret = fastboot_board_init(&fb_cfg, &vendor_fb_strings); ++ if (ret) ++ return ret; ++ if (!vendor_fb_strings) ++ return -EINVAL; ++ ++ ret = usb_gadget_init_udc(); ++ if (ret) { ++ printf("gadget probe failed\n"); ++ return 1; ++ } ++ udc_is_probbed = 1; ++ ++ ret = usb_gadget_register_driver(&fast_gadget); ++ if (ret) { ++ printf("Add gadget failed\n"); ++ goto err; ++ } ++ ++ gadget_is_connected = 1; ++ usb_gadget_handle_interrupts(); ++ return 0; ++ ++err: ++ fastboot_shutdown(); ++ return 1; ++} ++ ++int fastboot_poll(void) ++{ ++ usb_gadget_handle_interrupts(); ++ ++ if (gadget_is_connected) ++ return 0; ++ else ++ return 1; ++} ++ ++void fastboot_shutdown(void) ++{ ++ if (!udc_is_probbed) ++ return; ++ udc_is_probbed = 0; ++ usb_gadget_exit_udc(); ++} ++ ++int fastboot_tx_write(const char *buffer, unsigned int buffer_size) ++{ ++ int ret; ++ ++ if (req_in->complete == NULL) ++ req_in->complete = fastboot_complete_in; ++ ++ memcpy(req_in->buf, buffer, buffer_size); ++ req_in->length = buffer_size; ++ ret = usb_ep_queue(ep_in, req_in, 0); ++ if (ret) ++ printf("Error %d on queue\n", ret); ++ return 0; ++} +diff --git a/drivers/usb/gadget/g_fastboot.h b/drivers/usb/gadget/g_fastboot.h +new file mode 100644 +index 0000000..ff2621c +--- /dev/null ++++ b/drivers/usb/gadget/g_fastboot.h +@@ -0,0 +1,20 @@ ++#ifndef _G_FASTBOOT_H_ ++#define _G_FASTBOOT_H_ ++ ++#define EP_BUFFER_SIZE 4096 ++#define FASTBOOT_INTERFACE_CLASS 0xff ++#define FASTBOOT_INTERFACE_SUB_CLASS 0x42 ++#define FASTBOOT_INTERFACE_PROTOCOL 0x03 ++#define FASTBOOT_VERSION "0.4" ++ ++extern struct fastboot_config fb_cfg; ++extern struct usb_ep *ep_in; ++extern struct usb_request *req_in; ++extern struct usb_ep *ep_out; ++extern struct usb_request *req_out; ++ ++void rx_handler_command(struct usb_ep *ep, struct usb_request *req); ++int fastboot_tx_write(const char *buffer, unsigned int buffer_size); ++const char *fb_find_usb_string(unsigned int id); ++ ++#endif +diff --git a/drivers/usb/gadget/u_fastboot.c b/drivers/usb/gadget/u_fastboot.c +new file mode 100644 +index 0000000..00762aa +--- /dev/null ++++ b/drivers/usb/gadget/u_fastboot.c +@@ -0,0 +1,308 @@ ++/* ++ * (C) Copyright 2008 - 2009 ++ * Windriver, ++ * Tom Rix ++ * ++ * Copyright (c) 2011 Sebastian Andrzej Siewior ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of ++ * the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, ++ * MA 02111-1307 USA ++ * ++ * Part of the rx_handler were copied from the Android project. ++ * Specifically rx command parsing in the usb_rx_data_complete ++ * function of the file bootable/bootloader/legacy/usbloader/usbloader.c ++ * ++ * The logical naming of flash comes from the Android project ++ * Thse structures and functions that look like fastboot_flash_* ++ * They come from bootable/bootloader/legacy/libboot/flash.c ++ * ++ * This is their Copyright: ++ * ++ * Copyright (C) 2008 The Android Open Source Project ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * ++ * 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 MERCHANTABILITY AND FITNESS ++ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ++ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, 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 DAMAGE. ++ */ ++#include ++#include ++#include ++#include ++#include "g_fastboot.h" ++ ++/* The 64 defined bytes plus \0 */ ++#define RESPONSE_LEN (64 + 1) ++ ++struct fastboot_config fb_cfg; ++ ++static unsigned int download_size; ++static unsigned int download_bytes; ++ ++static int fastboot_tx_write_str(const char *buffer) ++{ ++ return fastboot_tx_write(buffer, strlen(buffer)); ++} ++ ++static void compl_do_reset(struct usb_ep *ep, struct usb_request *req) ++{ ++ do_reset(NULL, 0, 0, NULL); ++} ++ ++static void cb_reboot(struct usb_ep *ep, struct usb_request *req) ++{ ++ req_in->complete = compl_do_reset; ++ fastboot_tx_write_str("OKAY"); ++} ++ ++static int strcmp_l1(const char *s1, const char *s2) ++{ ++ return strncmp(s1, s2, strlen(s1)); ++} ++ ++static void cb_getvar(struct usb_ep *ep, struct usb_request *req) ++{ ++ char *cmd = req->buf; ++ char response[RESPONSE_LEN]; ++ const char *s; ++ ++ strcpy(response, "OKAY"); ++ strsep(&cmd, ":"); ++ if (!cmd) { ++ fastboot_tx_write_str("FAILmissing var"); ++ return; ++ } ++ ++ if (!strcmp_l1("version", cmd)) { ++ strncat(response, FASTBOOT_VERSION, sizeof(response)); ++ ++ } else if (!strcmp_l1("downloadsize", cmd)) { ++ char str_num[12]; ++ ++ sprintf(str_num, "%08x", fb_cfg.transfer_buffer_size); ++ strncat(response, str_num, sizeof(response)); ++ ++ } else if (!strcmp_l1("product", cmd)) { ++ ++ s = fb_find_usb_string(FB_STR_PRODUCT_IDX); ++ if (s) ++ strncat(response, s, sizeof(response)); ++ else ++ strcpy(response, "FAILValue not set"); ++ ++ } else if (!strcmp_l1("serialno", cmd)) { ++ ++ s = fb_find_usb_string(FB_STR_SERIAL_IDX); ++ if (s) ++ strncat(response, s, sizeof(response)); ++ else ++ strcpy(response, "FAILValue not set"); ++ ++ } else if (!strcmp_l1("cpurev", cmd)) { ++ ++ s = fb_find_usb_string(FB_STR_PROC_REV_IDX); ++ if (s) ++ strncat(response, s, sizeof(response)); ++ else ++ strcpy(response, "FAILValue not set"); ++ } else if (!strcmp_l1("secure", cmd)) { ++ ++ s = fb_find_usb_string(FB_STR_PROC_TYPE_IDX); ++ if (s) ++ strncat(response, s, sizeof(response)); ++ else ++ strcpy(response, "FAILValue not set"); ++ } else { ++ strcpy(response, "FAILVariable not implemented"); ++ } ++ fastboot_tx_write_str(response); ++} ++ ++static unsigned int rx_bytes_expected(void) ++{ ++ int rx_remain = download_size - download_bytes; ++ if (rx_remain < 0) ++ return 0; ++ if (rx_remain > EP_BUFFER_SIZE) ++ return EP_BUFFER_SIZE; ++ return rx_remain; ++} ++ ++#define BYTES_PER_DOT 32768 ++static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req) ++{ ++ char response[RESPONSE_LEN]; ++ unsigned int transfer_size = download_size - download_bytes; ++ const unsigned char *buffer = req->buf; ++ unsigned int buffer_size = req->actual; ++ ++ if (req->status != 0) { ++ printf("Bad status: %d\n", req->status); ++ return; ++ } ++ ++ if (buffer_size < transfer_size) ++ transfer_size = buffer_size; ++ ++ memcpy(fb_cfg.transfer_buffer + download_bytes, ++ buffer, transfer_size); ++ ++ download_bytes += transfer_size; ++ ++ /* Check if transfer is done */ ++ if (download_bytes >= download_size) { ++ /* ++ * Reset global transfer variable, keep download_bytes because ++ * it will be used in the next possible flashing command ++ */ ++ download_size = 0; ++ req->complete = rx_handler_command; ++ req->length = EP_BUFFER_SIZE; ++ ++ sprintf(response, "OKAY"); ++ fastboot_tx_write_str(response); ++ ++ printf("\ndownloading of %d bytes finished\n", ++ download_bytes); ++ } else ++ req->length = rx_bytes_expected(); ++ ++ if (download_bytes && !(download_bytes % BYTES_PER_DOT)) { ++ printf("."); ++ if (!(download_bytes % (74 * BYTES_PER_DOT))) ++ printf("\n"); ++ ++ } ++ req->actual = 0; ++ usb_ep_queue(ep, req, 0); ++} ++ ++static void cb_download(struct usb_ep *ep, struct usb_request *req) ++{ ++ char *cmd = req->buf; ++ char response[RESPONSE_LEN]; ++ ++ strsep(&cmd, ":"); ++ download_size = simple_strtoul(cmd, NULL, 16); ++ download_bytes = 0; ++ ++ printf("Starting download of %d bytes\n", ++ download_size); ++ ++ if (0 == download_size) { ++ sprintf(response, "FAILdata invalid size"); ++ } else if (download_size > ++ fb_cfg.transfer_buffer_size) { ++ download_size = 0; ++ sprintf(response, "FAILdata too large"); ++ } else { ++ sprintf(response, "DATA%08x", download_size); ++ req->complete = rx_handler_dl_image; ++ req->length = rx_bytes_expected(); ++ } ++ fastboot_tx_write_str(response); ++} ++ ++static char boot_addr_start[32]; ++static char *bootm_args[] = { "bootm", boot_addr_start, NULL }; ++ ++static void do_bootm_on_complete(struct usb_ep *ep, struct usb_request *req) ++{ ++ req->complete = NULL; ++ fastboot_shutdown(); ++ printf("Booting kernel..\n"); ++ ++ do_bootm(NULL, 0, 2, bootm_args); ++ ++ /* This only happens if image is somehow faulty so we start over */ ++ do_reset(NULL, 0, 0, NULL); ++} ++ ++static void cb_boot(struct usb_ep *ep, struct usb_request *req) ++{ ++ sprintf(boot_addr_start, "0x%p", fb_cfg.transfer_buffer); ++ ++ req_in->complete = do_bootm_on_complete; ++ fastboot_tx_write_str("OKAY"); ++ return; ++} ++ ++struct cmd_dispatch_info { ++ char *cmd; ++ void (*cb)(struct usb_ep *ep, struct usb_request *req); ++}; ++ ++static struct cmd_dispatch_info cmd_dispatch_info[] = { ++ { ++ .cmd = "reboot", ++ .cb = cb_reboot, ++ }, { ++ .cmd = "getvar:", ++ .cb = cb_getvar, ++ }, { ++ .cmd = "download:", ++ .cb = cb_download, ++ }, { ++ .cmd = "boot", ++ .cb = cb_boot, ++ }, ++}; ++ ++void rx_handler_command(struct usb_ep *ep, struct usb_request *req) ++{ ++ char response[RESPONSE_LEN]; ++ char *cmdbuf = req->buf; ++ void (*func_cb)(struct usb_ep *ep, struct usb_request *req) = NULL; ++ int i; ++ ++ sprintf(response, "FAIL"); ++ ++ for (i = 0; i < ARRAY_SIZE(cmd_dispatch_info); i++) { ++ if (!strcmp_l1(cmd_dispatch_info[i].cmd, cmdbuf)) { ++ func_cb = cmd_dispatch_info[i].cb; ++ break; ++ } ++ } ++ ++ if (!func_cb) ++ fastboot_tx_write_str("FAILunknown command"); ++ else ++ func_cb(ep, req); ++ ++ if (req->status == 0) { ++ *cmdbuf = '\0'; ++ req->actual = 0; ++ usb_ep_queue(ep, req, 0); ++ } ++} +diff --git a/examples/standalone/Makefile b/examples/standalone/Makefile +index 9ab5446..e0f6f52 100644 +--- a/examples/standalone/Makefile ++++ b/examples/standalone/Makefile +@@ -40,6 +40,10 @@ ELF := $(addprefix $(obj)/,$(ELF)) + + gcclibdir := $(shell dirname `$(CC) -print-libgcc-file-name`) + ++ifeq ($(ARCH) , x86) ++gcclibdir := $(shell dirname $(NORMAL_LIBGCC)) ++endif ++ + # For PowerPC there's no need to compile standalone applications as a + # relocatable executable. The relocation data is not needed, and + # also causes the entry point of the standalone application to be +diff --git a/fs/fat/fat.c b/fs/fat/fat.c +index 54f42ea..23e99d3 100644 +--- a/fs/fat/fat.c ++++ b/fs/fat/fat.c +@@ -42,6 +42,7 @@ static disk_partition_t cur_part_info; + #define DOS_BOOT_MAGIC_OFFSET 0x1fe + #define DOS_FS_TYPE_OFFSET 0x36 + #define DOS_FS32_TYPE_OFFSET 0x52 ++#define DOS_PART_TBL_OFFSET 0x1be + + static int disk_read(__u32 block, __u32 nr_blocks, void *buf) + { +@@ -76,7 +77,37 @@ int fat_set_blk_dev(block_dev_desc_t *dev_desc, disk_partition_t *info) + return 0; + if (!memcmp(buffer + DOS_FS32_TYPE_OFFSET, "FAT32", 5)) + return 0; +- ++#ifdef CONFIG_FAT_MBR_SCAN ++ /* Test if it could be an MBR, and update start block to the ++ * first available primary partition ++ */ ++ unsigned char *part_desc = (buffer + DOS_PART_TBL_OFFSET); ++ int i = 0; ++ for (i = 0; i < 4; i++, part_desc+=16) { ++ /* Check part type to be a primary FAT partition*/ ++ if ((*(part_desc+4) == 0x1) || (*(part_desc+4) == 0x4) || ++ (*(part_desc+4) == 0x6) || (*(part_desc+4) == 0xb) || ++ (*(part_desc+4) == 0xc) || (*(part_desc+4) == 0xe) ){ ++ int lba_start = (*(part_desc + 8 + 3) << 24) + ++ (*(part_desc + 8 + 2) << 16) + ++ (*(part_desc + 8 + 1) << 8) + ++ *(part_desc + 8 + 0); ++ int lba_size = (*(part_desc + 12 + 3) << 24) + ++ (*(part_desc + 12 + 2) << 16) + ++ (*(part_desc + 12 + 1) << 8) + ++ *(part_desc + 12 + 0); ++ debug("Found partition in MBR lba start:%d lba size:%d\n", ++ lba_start, lba_size); ++ debug("Old partition info lba start:"LBAFU" size:"LBAFU"\n", ++ cur_part_info.start, cur_part_info.size); ++ cur_part_info.start += lba_start; ++ cur_part_info.size = lba_size; ++ debug("New partition info lba start:"LBAFU" size:"LBAFU"\n", ++ cur_part_info.start, cur_part_info.size); ++ return 0; ++ } ++ } ++#endif + cur_dev = NULL; + return -1; + } +@@ -185,7 +216,7 @@ static __u32 get_fatent(fsdata *mydata, __u32 entry) + } + + debug("FAT%d: entry: 0x%04x = %d, offset: 0x%04x = %d\n", +- mydata->fatsize, entry, entry, offset, offset); ++ mydata->fatsize, entry, entry, offset, offset); + + /* Read a new block of FAT entries into the cache. */ + if (bufnum != mydata->fatbufnum) { +@@ -246,7 +277,7 @@ static __u32 get_fatent(fsdata *mydata, __u32 entry) + break; + } + debug("FAT%d: ret: %08x, offset: %04x\n", +- mydata->fatsize, ret, offset); ++ mydata->fatsize, ret, offset); + + return ret; + } +diff --git a/include/android_image.h b/include/android_image.h +new file mode 100644 +index 0000000..5af32c5 +--- /dev/null ++++ b/include/android_image.h +@@ -0,0 +1,102 @@ ++/* ++ * This is from the Android Project, ++ * bootloader/legacy/include/boot/bootimg.h ++ * ++ * Copyright (C) 2008 The Android Open Source Project ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * ++ * 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 MERCHANTABILITY AND FITNESS ++ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ++ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, 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 DAMAGE. ++ */ ++ ++#ifndef _ANDROID_IMAGE_H_ ++#define _ANDROID_IMAGE_H_ ++ ++#define ANDR_BOOT_MAGIC "ANDROID!" ++#define ANDR_BOOT_MAGIC_SIZE 8 ++#define ANDR_BOOT_NAME_SIZE 16 ++#define ANDR_BOOT_ARGS_SIZE 512 ++ ++struct andr_img_hdr { ++ u8 magic[ANDR_BOOT_MAGIC_SIZE]; ++ ++ u32 kernel_size; /* size in bytes */ ++ u32 kernel_addr; /* physical load addr */ ++ ++ u32 ramdisk_size; /* size in bytes */ ++ u32 ramdisk_addr; /* physical load addr */ ++ ++ u32 second_size; /* size in bytes */ ++ u32 second_addr; /* physical load addr */ ++ ++ u32 tags_addr; /* physical addr for kernel tags */ ++ u32 page_size; /* flash page size we assume */ ++ u32 unused[2]; /* future expansion: should be 0 */ ++ ++ char name[ANDR_BOOT_NAME_SIZE]; /* asciiz product name */ ++ ++ char cmdline[ANDR_BOOT_ARGS_SIZE]; ++ ++ u32 id[8]; /* timestamp / checksum / sha1 / etc */ ++}; ++ ++#ifdef CONFIG_ANDROID_BOOT_IMAGE ++u32 android_img_get_end(struct andr_img_hdr *hdr); ++u32 android_img_get_kload(struct andr_img_hdr *hdr); ++int android_image_get_kernel(struct andr_img_hdr *hdr, int verify); ++#else ++static inline u32 android_img_get_end(struct andr_img_hdr *hdr) { return 0; } ++static inline u32 android_img_get_kload(struct andr_img_hdr *hdr) { return 0; } ++static inline int android_image_get_kernel(struct andr_img_hdr *hdr, int verify) ++{ ++ return -1; ++} ++#endif ++ ++/* ++ * +-----------------+ ++ * | boot header | 1 page ++ * +-----------------+ ++ * | kernel | n pages ++ * +-----------------+ ++ * | ramdisk | m pages ++ * +-----------------+ ++ * | second stage | o pages ++ * +-----------------+ ++ * ++ * n = (kernel_size + page_size - 1) / page_size ++ * m = (ramdisk_size + page_size - 1) / page_size ++ * o = (second_size + page_size - 1) / page_size ++ * ++ * 0. all entities are page_size aligned in flash ++ * 1. kernel and ramdisk are required (size != 0) ++ * 2. second is optional (second_size == 0 -> no second) ++ * 3. load each element (kernel, ramdisk, second) at ++ * the specified physical address (kernel_addr, etc) ++ * 4. prepare tags at tag_addr. kernel_args[] is ++ * appended to the kernel commandline in the tags. ++ * 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr ++ * 6. if second_size != 0: jump to second_addr ++ * else: jump to kernel_addr ++ */ ++#endif +diff --git a/include/configs/coreboot.h b/include/configs/coreboot.h +index d1d732f..b5b1f7f 100644 +--- a/include/configs/coreboot.h ++++ b/include/configs/coreboot.h +@@ -27,6 +27,7 @@ + #define CONFIG_SYS_EARLY_PCI_INIT + + #define CONFIG_LMB ++#define CONFIG_USE_FDT + #define CONFIG_OF_LIBFDT + #define CONFIG_OF_CONTROL + #define CONFIG_OF_SEPARATE +diff --git a/include/configs/edison.h b/include/configs/edison.h +new file mode 100644 +index 0000000..fe0759f +--- /dev/null ++++ b/include/configs/edison.h +@@ -0,0 +1,268 @@ ++#ifndef _EDISON_H ++#define _EDISON_H ++ ++#define CONFIG_WATCHDOG ++#define CONFIG_WATCHDOG_HEARTBEAT 30 ++#define CONFIG_SFI ++#define CONFIG_BOARD_LATE_INIT ++ ++/*----------------------------------------------------------------------- ++ * Misc ++ */ ++ ++#define CONFIG_CMD_ITEST ++#define CONFIG_CMD_ASKENV ++#define CONFIG_CMD_BDI ++#define CONFIG_CMD_BSP ++#define CONFIG_CMD_BOOTD ++#define CONFIG_CMD_CACHE ++#define CONFIG_CMD_CONSOLE ++/* ++ *#define CONFIG_CMD_DATE ++ */ ++#define CONFIG_CMD_DIAG ++#define CONFIG_CMD_ECHO ++#define CONFIG_CMD_EDITENV ++#define CONFIG_CMD_ELF ++#define CONFIG_CMD_ENV_CALLBACK ++#define CONFIG_CMD_ENV_FLAGS ++#define CONFIG_CMD_ENV_EXISTS ++/* ++ *#define CONFIG_CMD_GETTIME ++ */ ++#define CONFIG_CMD_GREPENV ++#define CONFIG_CMD_HASH ++#define CONFIG_CMD_INI ++/* ++ *#define CONFIG_CMD_KGDB ++ */ ++/* ++ *#define CONFIG_CMD_MD5SUM ++ */ ++#define CONFIG_CMD_MEMINFO ++#define CONFIG_CMD_MEMORY ++#define CONFIG_CMD_PORTIO ++#define CONFIG_CMD_READ ++#define CONFIG_CMD_REGINFO ++/* ++ *#define CONFIG_CMD_I2C ++ *#define CONFIG_SYS_I2C_SPEED 50000 ++ */ ++/* ++ *#define CONFIG_CMD_SHA1SUM ++ */ ++#define CONFIG_CMD_SOURCE ++/* ++ *#define CONFIG_CMD_SPI ++ */ ++#define CONFIG_CMD_TIMER ++ ++/* ++ *#define CONFIG_CMD_TRACE ++ */ ++/* ++ *#define CONFIG_CMD_DATE ++ */ ++#define CONFIG_CMD_ECHO ++/* ++ *#define CONFIG_CMD_GPIO ++ */ ++#define CONFIG_CMD_LOADB ++#define CONFIG_CMD_LOADS ++#define CONFIG_CMD_IRQ ++#define CONFIG_CMD_MEMORY ++#define CONFIG_CMD_MISC ++#define CONFIG_CMD_PCI ++#define CONFIG_CMD_SOURCE ++/* ++ *#define CONFIG_CMD_TIME ++ *#define CONFIG_CMD_GETTIME ++ */ ++/* ++ *#define CONFIG_CMD_USB ++ */ ++/* ++ *#define EARLY_TRACE ++ *#define FTRACE ++ */ ++ ++ ++/*----------------------------------------------------------------------- ++ * Boot ++ */ ++ ++#define CONFIG_ZBOOT_32 ++#define CONFIG_CMD_ZBOOT ++#define CONFIG_AUTOBOOT ++#define CONFIG_BOOTCOMMAND "run bootcmd" ++#define CONFIG_BOOTDELAY 3 ++ ++/*----------------------------------------------------------------------- ++ * DEBUG ++ */ ++ ++/* ++ *#define DEBUG ++ */ ++ ++/* ++ *#define CONFIG_PRE_CONSOLE_BUFFER ++ *#define CONFIG_PRE_CON_BUF_SZ (1024*1024*2) ++ *#define CONFIG_PRE_CON_BUF_ADDR 0x29200000 ++ */ ++ ++/*----------------------------------------------------------------------- ++ * Serial ++ */ ++ ++#define CONFIG_SERIAL ++#define CONFIG_SYS_TNG_SERIAL ++#define CONFIG_SYS_TNG_SERIAL2 ++#define CONFIG_BAUDRATE 115200 ++ ++/* ++* MMC ++ */ ++#define CONFIG_MD5 ++#define CONFIG_GENERIC_MMC ++#define CONFIG_MMC ++#define CONFIG_SDHCI ++#define CONFIG_TANGIER_SDHCI ++#define CONFIG_CMD_MMC ++#define CONFIG_MMC_SDMA ++/*#define CONFIG_MMC_TRACE*/ ++ ++/************************************************************ ++ * DISK Partition support ++ ************************************************************/ ++#define CONFIG_EFI_PARTITION ++#define CONFIG_DOS_PARTITION ++#define CONFIG_MAC_PARTITION ++ ++#define CONFIG_FS_FAT ++#define CONFIG_CMD_FAT ++#define CONFIG_FAT_MBR_SCAN ++#define CONFIG_FAT_WRITE ++ ++#define CONFIG_CMD_GPT ++#define CONFIG_CMD_PART ++#define CONFIG_CMD_EXT4 ++#define CONFIG_CMD_EXT4_WRITE ++#define CONFIG_PARTITION_UUIDS ++#define CONFIG_RANDOM_UUID ++#define CONFIG_CMD_FS_GENERIC ++ /* ++ * Miscellaneous configurable options ++ */ ++#define CONFIG_SYS_LONGHELP ++#define CONFIG_SYS_PROMPT "boot > " ++#define CONFIG_SYS_CBSIZE 2048 ++#define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE + \ ++ sizeof(CONFIG_SYS_PROMPT) + \ ++ 16) ++#define CONFIG_SYS_MAXARGS 128 ++#define CONFIG_SYS_BARGSIZE CONFIG_SYS_CBSIZE ++#define CONFIG_AUTO_COMPLETE ++#define CONFIG_SYS_HUSH_PARSER ++ ++#define CONFIG_SHA1 ++#define CONFIG_CMD_SHA1SUM ++/*----------------------------------------------------------------------- ++ * Board Features ++ */ ++ ++#define CONFIG_SYS_NO_FLASH ++#define CONFIG_INHERIT_GDT ++ ++/*----------------------------------------------------------------------- ++ * Memory ++ */ ++ ++#define CONFIG_SYS_LOAD_ADDR 0x100000 ++#define CONFIG_PHYSMEM ++ ++#define CONFIG_SYS_CACHELINE_SIZE 64 ++ ++#define CONFIG_NR_DRAM_BANKS 3 ++ ++#define CONFIG_SYS_STACK_SIZE (32 * 1024) ++ ++#define CONFIG_SYS_CAR_ADDR 0x19200000 ++#define CONFIG_SYS_CAR_SIZE (16 * 1024) ++ ++#define CONFIG_SYS_MONITOR_BASE CONFIG_SYS_TEXT_BASE ++#define CONFIG_SYS_MONITOR_LEN (256 * 1024) ++ ++#define CONFIG_SYS_MALLOC_LEN ( 128 * 1024 * 1024) ++ ++#define CONFIG_SYS_HEAP_SIZE (128*1024*1024) ++#define CONFIG_SYS_HEAP_MINI_SIZE (5*1024*1024) ++ ++#define CONFIG_SYS_MEMTEST_START 0x00100000 ++#define CONFIG_SYS_MEMTEST_END 0x01000000 ++ ++/*----------------------------------------------------------------------- ++ * CPU Features ++ */ ++ ++#define CONFIG_SYS_X86_TSC_TIMER ++#define CONFIG_SYS_NUM_IRQS 16 ++#define CONFIG_PCI ++#define CONFIG_SYS_PCAT_INTERRUPTS ++ ++#define CONFIG_CMD_GETTIME ++#define CONFIG_INTEL_MID ++ ++/*----------------------------------------------------------------------- ++ * Environment ++ */ ++#define CONFIG_ENV_IS_IN_MMC ++#define CONFIG_SYS_MMC_ENV_DEV 0 ++#define CONFIG_SYS_MMC_ENV_PART 0 ++#define CONFIG_ENV_SIZE (64*1024) ++#define CONFIG_ENV_OFFSET (3 * 1024 * 1024) ++#define CONFIG_ENV_OFFSET_REDUND (6 * 1024 * 1024) ++#define CONFIG_CMD_SAVEENV ++#define CONFIG_CMD_RUN ++#define CONFIG_SUPPORT_EMMC_BOOT ++#define CONFIG_CMD_SETEXPR ++ ++/*----------------------------------------------------------------------- ++ * USB ++ */ ++#define iounmap(x) ++#define CONFIG_USB_DWC3 ++#define CONFIG_USB_DWC3_UDC_REGS (void *) 0xf9100000 ++#define CONFIG_USB_DWC3_UDC_REGS_END (void *) 0xf9100400 ++ ++ ++#define CONFIG_USB_DWC3_GADGET ++#define CONFIG_USB_DEVICE ++#define CONFIG_USB_GADGET ++#define CONFIG_USB_GADGET_VBUS_DRAW 2 ++#define CONFIG_USB_GADGET_DUALSPEED ++ ++#define CONFIG_USBDOWNLOAD_GADGET ++#define CONFIG_G_DNL_MANUFACTURER "Intel" ++#define CONFIG_G_DNL_VENDOR_NUM 0x8087 ++#define CONFIG_G_DNL_PRODUCT_NUM 0x0a99 ++ ++#define CONFIG_DFU_FUNCTION ++#define CONFIG_CMD_DFU ++#define CONFIG_DFU_TIMEOUT ++#define CONFIG_DFU_MMC ++#define CONFIG_DFU_RAM ++ ++/*----------------------------------------------------------------------- ++ * SCU ++ */ ++ ++#define CONFIG_INTEL_SCU ++#define CONFIG_SCU_BASE_ADDR 0xff000000 ++#define CONFIG_SCU_IPC_BASE 0xff009000 ++#define CONFIG_SCU_I2C_BASE 0xff00d000 ++#define CONFIG_CPU_CHIP 4 ++#define CONFIG_X86_MRFLD ++ ++ ++#endif +diff --git a/include/dfu.h b/include/dfu.h +index 6c71ecb..d3fdf97 100644 +--- a/include/dfu.h ++++ b/include/dfu.h +@@ -37,12 +37,17 @@ enum dfu_op { + DFU_OP_WRITE, + }; + ++#define DFU_NOT_SUPPORTED -1 ++ + struct mmc_internal_data { + /* RAW programming */ + unsigned int lba_start; + unsigned int lba_size; + unsigned int lba_blk_size; + ++ /* Partition access */ ++ int partition_access; ++ + /* FAT/EXT */ + unsigned int dev; + unsigned int part; +@@ -132,8 +137,10 @@ const char *dfu_get_layout(enum dfu_layout l); + struct dfu_entity *dfu_get_entity(int alt); + char *dfu_extract_token(char** e, int *n); + void dfu_trigger_reset(void); ++void dfu_trigger_enum_done(void); + int dfu_get_alt(char *name); + bool dfu_reset(void); ++bool dfu_enum_done(void); + int dfu_init_env_entities(char *interface, int dev); + unsigned char *dfu_get_buf(void); + unsigned char *dfu_free_buf(void); +diff --git a/include/intel_scu_ipc.h b/include/intel_scu_ipc.h +new file mode 100644 +index 0000000..a9cf406 +--- /dev/null ++++ b/include/intel_scu_ipc.h +@@ -0,0 +1,69 @@ ++#ifndef _INTEL_SCU_IPC_H_ ++#define _INTEL_SCU_IPC_H_ ++ ++/* IPC defines the following message types */ ++#define IPCMSG_WARM_RESET 0xF0 ++#define IPCMSG_COLD_RESET 0xF1 ++#define IPCMSG_SOFT_RESET 0xF2 ++#define IPCMSG_COLD_BOOT 0xF3 ++#define IPCMSG_GET_FW_REVISION 0xF4 ++#define IPCMSG_WATCHDOG_TIMER 0xF8 /* Set Kernel Watchdog Threshold */ ++ ++#define IPC_ERR_NONE 0 ++#define IPC_ERR_CMD_NOT_SUPPORTED 1 ++#define IPC_ERR_CMD_NOT_SERVICED 2 ++#define IPC_ERR_UNABLE_TO_SERVICE 3 ++#define IPC_ERR_CMD_INVALID 4 ++#define IPC_ERR_CMD_FAILED 5 ++#define IPC_ERR_EMSECURITY 6 ++ ++/* Command id associated with message IPCMSG_VRTC */ ++#define IPC_CMD_VRTC_SETTIME 1 /* Set time */ ++#define IPC_CMD_VRTC_SETALARM 2 /* Set alarm */ ++#define IPC_CMD_VRTC_SYNC_RTC 3 /* Sync MSIC/PMIC RTC to VRTC */ ++ ++union ipc_ifwi_version { ++ u8 raw[16]; ++ struct { ++ u16 ifwi_minor; ++ u8 ifwi_major; ++ u8 hardware_id; ++ u32 reserved[3]; ++ } fw __attribute__((packed)); ++} __attribute__((packed)); ++ ++/* Issue commands to the SCU with or without data */ ++void intel_scu_ipc_send_command(u32 cmd); ++int intel_scu_ipc_check_status(void); ++int intel_scu_ipc_simple_command(int cmd, int sub); ++void intel_scu_ipc_lock(void); ++void intel_scu_ipc_unlock(void); ++int intel_scu_ipc_command(u32 cmd, u32 sub, u8 * in, u8 inlen, ++ u32 * out, u32 outlen); ++int intel_scu_ipc_raw_cmd(u32 cmd, u32 sub, u8 * in, u8 inlen, ++ u32 * out, u32 outlen, u32 dptr, u32 sptr); ++ ++ ++void ipc_data_writel(u32 data, u32 offset); /* Write ipc data */ ++u32 ipc_read_status(void); ++u8 ipc_data_readb(u32 offset); /* Read ipc byte data */ ++u32 ipc_data_readl(u32 offset); /* Read ipc u32 data */ ++int intel_scu_ipc_command(u32 cmd, u32 sub, u8 * in, u8 inlen, u32 * out, ++ u32 outlen); ++int init_scu_ipc(void); ++ ++enum intel_mid_cpu_type { ++ INTEL_CPU_CHIP_NOTMID = 0, ++ INTEL_MID_CPU_CHIP_LINCROFT, ++ INTEL_MID_CPU_CHIP_PENWELL, ++ INTEL_MID_CPU_CHIP_CLOVERVIEW, ++ INTEL_MID_CPU_CHIP_TANGIER, ++}; ++ ++static inline enum intel_mid_cpu_type intel_mid_identify_cpu(void) ++{ ++ return INTEL_MID_CPU_CHIP_TANGIER; ++} ++ ++ ++#endif //_INTEL_SCU_IPC_H_ +diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h +index a8a5763..118a736 100644 +--- a/include/linux/usb/gadget.h ++++ b/include/linux/usb/gadget.h +@@ -20,6 +20,7 @@ + + #include + #include ++#include + + struct usb_ep; + +@@ -95,6 +96,7 @@ struct usb_request { + + int status; + unsigned actual; ++ unsigned stream_id; + }; + + /*-------------------------------------------------------------------------*/ +@@ -145,6 +147,8 @@ struct usb_ep { + const struct usb_ep_ops *ops; + struct list_head ep_list; + unsigned maxpacket:16; ++ unsigned max_streams:16; ++ unsigned maxburst:16; + }; + + /*-------------------------------------------------------------------------*/ +@@ -412,7 +416,6 @@ struct usb_gadget_ops { + + struct device { + void *driver_data; /* data private to the driver */ +- void *device_data; /* data private to the device */ + }; + + /** +@@ -483,11 +486,6 @@ static inline void *get_gadget_data(struct usb_gadget *gadget) + return gadget->dev.driver_data; + } + +-static inline struct usb_gadget *dev_to_usb_gadget(struct device *dev) +-{ +- return container_of(dev, struct usb_gadget, dev); +-} +- + /* iterates the non-control endpoints; 'tmp' is a struct usb_ep pointer */ + #define gadget_for_each_ep(tmp, gadget) \ + list_for_each_entry(tmp, &(gadget)->ep_list, ep_list) +@@ -861,4 +859,9 @@ extern void usb_ep_autoconfig_reset(struct usb_gadget *); + + extern int usb_gadget_handle_interrupts(void); + ++extern int usb_gadget_init_udc(void); ++extern void usb_gadget_exit_udc(void); ++extern int usb_gadget_probe_driver(struct usb_gadget_driver *driver, ++ int (*bind)(struct usb_gadget *)); ++ + #endif /* __LINUX_USB_GADGET_H */ +diff --git a/include/usb/fastboot.h b/include/usb/fastboot.h +new file mode 100644 +index 0000000..e55149a +--- /dev/null ++++ b/include/usb/fastboot.h +@@ -0,0 +1,100 @@ ++/* ++ * (C) Copyright 2008 - 2009 ++ * Windriver, ++ * Tom Rix ++ * ++ * Copyright (c) 2011 Sebastian Andrzej Siewior ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of ++ * the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, ++ * MA 02111-1307 USA ++ * ++ * The logical naming of flash comes from the Android project ++ * Thse structures and functions that look like fastboot_flash_* ++ * They come from bootloader/legacy/include/boot/flash.h ++ * ++ * The boot_img_hdr structure and associated magic numbers also ++ * come from the Android project. They are from ++ * bootloader/legacy/include/boot/bootimg.h ++ * ++ * Here are their copyrights ++ * ++ * Copyright (C) 2008 The Android Open Source Project ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * ++ * 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 MERCHANTABILITY AND FITNESS ++ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ++ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, 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 DAMAGE. ++ * ++ */ ++ ++#ifndef FASTBOOT_H ++#define FASTBOOT_H ++ ++#include ++#include ++#include ++#include ++#include ++ ++struct fastboot_config { ++ ++ /* ++ * Transfer buffer for storing data sent by the client. It should be ++ * able to hold a kernel image and flash partitions. Care should be ++ * take so it does not overrun bootloader memory ++ */ ++ unsigned char *transfer_buffer; ++ ++ /* Size of the buffer mentioned above */ ++ unsigned int transfer_buffer_size; ++}; ++ ++#define FB_STR_PRODUCT_IDX 1 ++#define FB_STR_SERIAL_IDX 2 ++#define FB_STR_CONFIG_IDX 3 ++#define FB_STR_INTERFACE_IDX 4 ++#define FB_STR_MANUFACTURER_IDX 5 ++#define FB_STR_PROC_REV_IDX 6 ++#define FB_STR_PROC_TYPE_IDX 7 ++ ++#if (CONFIG_CMD_FASTBOOT) ++ ++int fastboot_init(void); ++void fastboot_shutdown(void); ++int fastboot_poll(void); ++ ++int fastboot_board_init(struct fastboot_config *interface, ++ struct usb_gadget_strings **str); ++#endif ++#endif +diff --git a/pft-config.xml b/pft-config.xml +new file mode 100644 +index 0000000..885875f +--- /dev/null ++++ b/pft-config.xml +@@ -0,0 +1,28 @@ ++ ++ ++ system ++ saltbay ++ ++ 0x80000007 ++ ++ ++ ++ ifwi_saltbay_pr2.bin ++ 0003.008d ++ ++ ++ dnx_fwr_saltbay_pr2.bin ++ 0003.008d ++ ++ ++ dnx_osr_saltbay_pr2.bin ++ 0003.008d ++ ++ ++ ++ ++ u-boot.bin.osip ++ main-weekly-1115 ++ ++ ++ +-- +1.7.9.5 + diff --git a/device-software/meta-edison/recipes-bsp/u-boot/u-boot-fw-utils_2014.04.bb b/device-software/meta-edison/recipes-bsp/u-boot/u-boot-fw-utils_2014.04.bb new file mode 100644 index 0000000..0c2353c --- /dev/null +++ b/device-software/meta-edison/recipes-bsp/u-boot/u-boot-fw-utils_2014.04.bb @@ -0,0 +1,28 @@ +DESCRIPTION = "U-boot bootloader fw_printenv/setenv utils" +LICENSE = "GPLv2+" +LIC_FILES_CHKSUM = "file://Licenses/README;md5=025bf9f768cbcb1a165dbe1a110babfb" +SECTION = "bootloader" + +require u-boot-internal.inc + +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" +SRC_URI += "file://fw_env.config" + +EXTRA_OEMAKE = 'CROSS_COMPILE=${TARGET_PREFIX} CC="${TARGET_PREFIX}gcc ${TOOLCHAIN_OPTIONS}"' + +do_compile () { + oe_runmake ${UBOOT_MACHINE} + oe_runmake env +} + +do_install () { + install -d ${D}${sbindir} + install -m 755 ${S}/tools/env/fw_printenv_unstripped ${D}${sbindir}/fw_printenv + # This is not a typo, this tool checks the args[0] to change its behavior a-la-busybox + install -m 755 ${S}/tools/env/fw_printenv_unstripped ${D}${sbindir}/fw_setenv +} + +FILES_${PN} = "${sbindir}/*" +FILES_${PN} += "${sysconfdir}/fw_env.config" + +DEPENDS = "u-boot" diff --git a/device-software/meta-edison/recipes-bsp/u-boot/u-boot-internal.inc b/device-software/meta-edison/recipes-bsp/u-boot/u-boot-internal.inc new file mode 100644 index 0000000..35698d7 --- /dev/null +++ b/device-software/meta-edison/recipes-bsp/u-boot/u-boot-internal.inc @@ -0,0 +1,10 @@ +PV = "2014.04-1" +S = "${WORKDIR}/git" + +PACKAGE_ARCH = "${MACHINE_ARCH}" + +SRC_URI = "git://git.denx.de/u-boot.git;branch=master" +SRC_URI += "file://upstream_to_edison.patch" +SRC_URI += "file://${MACHINE}.env" +SRC_URI += "file://target_env/*.env" +SRCREV = "dda0dbfc69f3d560c87f5be85f127ed862ea6721" diff --git a/device-software/meta-edison/recipes-bsp/u-boot/u-boot-osip.inc b/device-software/meta-edison/recipes-bsp/u-boot/u-boot-osip.inc new file mode 100644 index 0000000..d05d440 --- /dev/null +++ b/device-software/meta-edison/recipes-bsp/u-boot/u-boot-osip.inc @@ -0,0 +1,208 @@ +OSIP_STARTING_LBA = "2048" +OSIP_DESTINATION_POINTER = "17825792" +OSIP_HANDOFF_POINTER = "17829888" + +# env is U-Boot primary environment where internal U-Boot variables are stored +# this default generated binary environment, is embedded in a raw OSIP image +# use by XFSTK tools to do the initial boot +ENV_IMAGE = "${S}/env.bin" +BASE_IMAGE = "${S}/u-boot.bin" +UBOOT_OSIP_SUFFIX = "img" +UBOOT_TMP_IMG = "${S}/u-boot.${UBOOT_OSIP_SUFFIX}.no-osip" +UBOOT_IMG = "${S}/u-boot.${UBOOT_OSIP_SUFFIX}" +UBOOT_OSIP_IMAGE = "u-boot-${MACHINE}-${PV}-${PR}.${UBOOT_OSIP_SUFFIX}" +UBOOT_OSIP_BINARY = "u-boot.${UBOOT_OSIP_SUFFIX}" +UBOOT_OSIP_SYMLINK = "u-boot-${MACHINE}.${UBOOT_OSIP_SUFFIX}" + + +do_uboot_padding() { + # U-Boot.bin map + # 0x000000 - 0x001000 | padding with 0 + # 0x001000 - 0x200000 | u-boot0 + dd if=/dev/zero of=u-boot.padding bs=4096 count=1 + cat u-boot.padding ${BASE_IMAGE} | dd of=u-boot.bin.padded + + # Align u-boot.bin to 4K for dfu + filesize=$(stat -c %s u-boot.bin.padded) + alignment=$(echo "4096 - (${filesize} % 4096)" | bc) + if [ ${alignment} -ne 0 ]; + then + dd if=/dev/zero of=u-boot.bin.padded bs=1 count=${alignment} conv=notrunc seek=${filesize} + fi + + cp u-boot.bin.padded ${BASE_IMAGE} +} + +do_uboot_uboot_env_mkimage() { + + # U-Boot.img map + # 0x000000 - 0x001000 | padding with 0 + # 0x001000 - 0x200000 | u-boot0 + # 0x200000 - 0x300000 | primary environment + # 0x300000 - 0x500000 | reserved + # 0x500000 - 0x600000 | secondary environment + + # U-Boot.img on eMMC in LBA (LBA size: 512 bytes) + # | description | OSII | GPT label + # 0x000000 - 0x000001 | MBR + OSIP | - | - + # 0x000001 - 0x000022 | GPT | - | - + # 0x000022 - 0x000800 | padding with 0 (alignment) | - | - + # 0x000800 - 0x001800 | u-boot0 | 1 | u-boot0 + # 0x001800 - 0x002600 | primary environment | - | u-boot-env0 + # 0x002600 - 0x003600 | u-boot1 | 2 | u-boot1 + # 0x003600 - 0x004400 | secondary environment | - | u-boot-env1 + + + # Fill U-Boot.img with 0 + dd if=/dev/zero of=${UBOOT_TMP_IMG} bs=6M count=1 + # copy u-boot.bin in u-boot.img (u-boot0) + dd if=${BASE_IMAGE} of=${UBOOT_TMP_IMG} bs=1M conv=notrunc + # copy (offset 2M) u-boot_env0.bin in u-boot.img (u-boot0) + dd if=${ENV_IMAGE} of=${UBOOT_TMP_IMG} bs=1M conv=notrunc seek=2 + # copy (offset 5M) u-boot_env1.bin in u-boot.img (u-boot0) + dd if=${ENV_IMAGE} of=${UBOOT_TMP_IMG} bs=1M conv=notrunc seek=5 +} + +python do_osip_mkimage() { + # + # Stitch an image to create an OSIP image (OS Image Profile). + # This script currently supports only one OSII (OS Image Identifier) + # If more is necessary, it will need to be adjusted. + # + # This is the C struct for one OSII (size = 24 bytes) + # struct OSII { //os image identifier + # uint16_t os_rev_minor; + # uint16_t os_rev_major; + # uint32_t logical_start_block; //units defined by get_block_size() if + # //reading/writing to/from nand, units of + # //512 bytes if cracking a stitched image + # uint32_t ddr_load_address; + # uint32_t entry_point; + # uint32_t size_of_os_image; //units defined by get_page_size() if + # //reading/writing to/from nand, units of + # //512 bytes if cracking a stitched image + # uint8_t attribute; + # uint8_t reserved[3]; + # }; + + # This is what a full OSIP header contains + # Its size is 512 bytes + # Offset Size (bytes) Description + # 0x000 4 OSIP Signature "$OS$" + # 0x004 1 Reserved + # 0x005 1 Header minor revision + # 0x006 1 Header major revision + # 0x007 1 Header checksum + # 0x008 1 Number of pointers + # 0x009 1 Number of images + # 0x00a 2 Header size + # 0x00c 20 Reserved + # 0x020 24 1st bootable image descriptor (OSII) + # 0x038 24 2nd bootable image descriptor (OSII) + # ... ... ... + # 0x170 24 15th bootable image descriptor (OSII) + # 0x188 48 Not used + # 0x1B8 4 Disk signature + # 0x1BC 2 Null (0x0000) + # 0x1BE 16 1st primary partition descriptor + # 0x1CE 16 2nd primary partition descriptor + # 0x1DE 16 3rd primary partition descriptor + # 0x1EE 16 4th primary partition descriptor + # 0x1FE 1 0x55 + # 0x1FF 1 0xaa + + import os + import sys + import argparse + import struct + + # As only a small portion of the OSIP header needs to be changed + # we simply store a full binary copy and we'll change just the + # necessary values in it + OSIP_HEADER_HEX_DATA = \ + '244f532400000121010138000000000000000000000000000000000000' \ + '00000000000000220000000000100100101001380100000f000000ffff' \ + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' \ + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' \ + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' \ + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' \ + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' \ + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' \ + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' \ + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' \ + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' \ + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' \ + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' \ + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' \ + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' \ + 'ffffffffff00000000000000000000ee0000000100000000e0a3030000' \ + '0000000000000000000000000000000000000000000000000000000000' \ + '000000000000000000000000000000000055aa' + + def main(): + input_file = "${UBOOT_TMP_IMG}" + output_file = "${UBOOT_IMG}" + handoff_pointer = ${OSIP_HANDOFF_POINTER} + destination_pointer = ${OSIP_DESTINATION_POINTER} + starting_lba = ${OSIP_STARTING_LBA} + + in_file_data = open(input_file, 'rb').read() + + out_file = open(output_file, 'wb') + + # Write OSIP header data + osip_header_data = bytearray(OSIP_HEADER_HEX_DATA.decode('hex')) + + # Override some values + + # Overrides starting LBA of imge in eMMC + starting_lba_packed = struct.pack("I", starting_lba) + osip_header_data[0x24:0x27+1]=starting_lba_packed + + # Overrides destination pointer to image in DDR + destination_pointer_packed = struct.pack("I", destination_pointer) + osip_header_data[0x28:0x2B+1]=destination_pointer_packed + + # Overrides pointer to handoff entry point in image + handoff_pointer_packed = struct.pack("I", handoff_pointer) + osip_header_data[0x2C:0x2F+1]=handoff_pointer_packed + + # The file needs to be padded to be multiple of 512 bytes + + # Overrides passed image size (in unit of 512 bytes blocks) + padding_len_packed_block = struct.pack("I", ( (6*1024*1024) / 512)) + osip_header_data[0x30:0x33+1]=padding_len_packed_block + + # Compute XOR checksum + osip_header_size = 1*0x18+0x20 # 24 bytes per OSII + 32 bytes header + osip_header_data[0x07]=0 + crc = osip_header_data[0] + for i in range(1, osip_header_size): + crc ^= osip_header_data[i] + osip_header_data[0x07]=crc + + out_file.write(osip_header_data) + + # Write image content + out_file.write(in_file_data) + + out_file.close() + + return 0 + + main() +} + +do_deploy_append() { + install -d ${DEPLOYDIR} + install ${UBOOT_IMG} ${DEPLOYDIR}/${UBOOT_OSIP_IMAGE} + + cd ${DEPLOYDIR} + rm -f ${UBOOT_IMG} ${UBOOT_OSIP_SYMLINK} + ln -sf ${UBOOT_OSIP_IMAGE} ${UBOOT_OSIP_SYMLINK} + ln -sf ${UBOOT_OSIP_IMAGE} ${UBOOT_OSIP_BINARY} +} + +addtask uboot_padding before do_environment_mkimage before do_deploy after do_compile +addtask uboot_uboot_env_mkimage before do_osip_mkimage after do_environment_mkimage +addtask osip_mkimage before do_deploy after do_uboot_uboot_env_mkimage diff --git a/device-software/meta-edison/recipes-bsp/u-boot/u-boot-target-env.inc b/device-software/meta-edison/recipes-bsp/u-boot/u-boot-target-env.inc new file mode 100644 index 0000000..6dee3d1 --- /dev/null +++ b/device-software/meta-edison/recipes-bsp/u-boot/u-boot-target-env.inc @@ -0,0 +1,93 @@ +# Handle severals environments generation for u-boot +# and link the default one for Ifwi to u-boot-osip recipe + +#Env binary size +ENV_SIZE = "0x10000" + +#Env base Name +ENV_BASE_NAME = "${MACHINE}" +# Env base file correspond to common part of all environment +ENV_BASE_FILE = "${WORKDIR}/${ENV_BASE_NAME}.env" + +# Env directory is where target variant files are stored +ENV_DIR = "${WORKDIR}/target_env" +# Env composed is directory where target env are composed +# by concatening of base environment file and variant files +# pattern name is applied the resulting files: base-variant.env +ENV_COMPOSED_DIR = "${WORKDIR}/target_composed" +# Env bin is directory where target env are store in binary form +# filename follows pattern name above : base-variant.bin +ENV_BIN_DIR = "${WORKDIR}/target_env_bin" + +# Env deploy dir is the name of directory where binary envs will be deployed +ENV_DEPLOY_DIR="u-boot-envs" + +# Env deploy src dir is the name of directory where txt envs will be deployed +ENV_SRC_DEPLOY_DIR="u-boot-envs-src" + +# Env target to use for IFWI stitching process +ENV_IFWI_TARGET_NAME="ifwi" +# Env image is U-Boot primary environment (where internal U-Boot variables are stored) +# The same vairiable is also defined in u-boot-osip recip in charge of doing stitching +# process for IFWI +ENV_IMAGE = "${S}/env.bin" + +do_build_mkimage_tool () { + HOSTCC="${CC}" HOSTLD="${LD}" HOSTLDFLAGS="${LDFLAGS}" HOSTSTRIP=true oe_runmake tools +} + +python do_environment_mkimage() { + import subprocess + import shutil + # list env variant target files + target_root_dir = d.getVar('ENV_DIR',True) + env_files = os.listdir(target_root_dir ) + # builds absolute paths + env_files = [ os.path.join(target_root_dir,f) for f in env_files] + + env_bin_dir = d.getVar("ENV_BIN_DIR",True) + # cleans if it exists env_bin directory + shutil.rmtree(env_bin_dir, ignore_errors=True) + # create env bin directory + os.mkdir(env_bin_dir) + # if a previous env image used for osip process exists delete it + env_image = d.getVar('ENV_IMAGE',True) + try: + os.unlink(env_image) + except OSError: pass + print 'Building binary environments in : %s' % env_bin_dir + # iterate targets list to build binary environment files + for target_env in env_files : + # get only filename without path and extension + target_filename = os.path.splitext(os.path.basename(target_env))[0] + # build output file path with ext + target_bin = os.path.join(env_bin_dir, + d.getVar('ENV_BASE_NAME',True) + '-' + target_filename + '.bin') + # generated mkenvimage tool command line + cmd_mkimg ='cat %s %s | grep -v -E "^$|^\#" |' \ + ' ./tools/mkenvimage -s %s -r -o %s -' \ + % ( d.getVar('ENV_BASE_FILE',True),target_env, + d.getVar("ENV_SIZE",True), target_bin) + print 'Building binary for %s target:' % (target_filename) + print '%s' % cmd_mkimg + # execute shell command + ret = subprocess.call(cmd_mkimg, shell=True) + if ret: return ret + if d.getVar('ENV_IFWI_TARGET_NAME',True) in target_bin : + # create a symbolic link on default binary file env file to + # avoid modifying to much osip part + print 'Create for IFWI stitching symlink %s to %s' % (env_image, target_bin) + os.symlink(target_bin, env_image) + return 0 +} + +do_deploy_append() { + install -d ${DEPLOYDIR} + # deploy binary U-boot environments + echo "Deploying U-boot Environments binary files in ${DEPLOYDIR}/${ENV_DEPLOY_DIR}" + install -d ${DEPLOYDIR}/${ENV_DEPLOY_DIR} + cp ${ENV_BIN_DIR}/*.bin ${DEPLOYDIR}/${ENV_DEPLOY_DIR} +} + +addtask build_mkimage_tool after do_compile before do_environment_mkimage +addtask environment_mkimage after do_build_mkimage_tool before do_deploy diff --git a/device-software/meta-edison/recipes-bsp/u-boot/u-boot-tools_2014.04.bb b/device-software/meta-edison/recipes-bsp/u-boot/u-boot-tools_2014.04.bb new file mode 100644 index 0000000..ebbef01 --- /dev/null +++ b/device-software/meta-edison/recipes-bsp/u-boot/u-boot-tools_2014.04.bb @@ -0,0 +1,22 @@ +DESCRIPTION = "U-boot bootloader mkimage tool" +LICENSE = "GPLv2+" +LIC_FILES_CHKSUM = "file://Licenses/README;md5=025bf9f768cbcb1a165dbe1a110babfb" +SECTION = "bootloader" + +require u-boot-internal.inc + +EXTRA_OEMAKE = 'HOSTCC="${CC}" HOSTLD="${LD}" HOSTLDFLAGS="${LDFLAGS}" HOSTSTRIP=true' + +do_compile () { + oe_runmake tools +} + +do_install () { + install -d ${D}${bindir} + install -m 0755 tools/mkimage ${D}${bindir}/uboot-mkimage + install -m 0755 tools/mkenvimage ${D}${bindir}/uboot-mkenvimage + ln -sf uboot-mkimage ${D}${bindir}/mkimage + ln -sf uboot-mkenvimage ${D}${bindir}/mkenvimage +} + +BBCLASSEXTEND = "native nativesdk" diff --git a/device-software/meta-edison/recipes-bsp/u-boot/u-boot_2014.04.bb b/device-software/meta-edison/recipes-bsp/u-boot/u-boot_2014.04.bb new file mode 100644 index 0000000..3785576 --- /dev/null +++ b/device-software/meta-edison/recipes-bsp/u-boot/u-boot_2014.04.bb @@ -0,0 +1,7 @@ +require u-boot-internal.inc +require recipes-bsp/u-boot/u-boot.inc +require u-boot-target-env.inc +require u-boot-osip.inc + +LICENSE = "GPLv2+" +LIC_FILES_CHKSUM = "file://Licenses/README;md5=025bf9f768cbcb1a165dbe1a110babfb" diff --git a/device-software/meta-edison/recipes-kernel/linux/files/defconfig b/device-software/meta-edison/recipes-kernel/linux/files/defconfig index 7f3e626..b1ed708 100644 --- a/device-software/meta-edison/recipes-kernel/linux/files/defconfig +++ b/device-software/meta-edison/recipes-kernel/linux/files/defconfig @@ -153,7 +153,7 @@ CONFIG_NAMESPACES=y # CONFIG_IPC_NS is not set # CONFIG_USER_NS is not set # CONFIG_PID_NS is not set -# CONFIG_NET_NS is not set +CONFIG_NET_NS=y CONFIG_UIDGID_CONVERTED=y # CONFIG_UIDGID_STRICT_TYPE_CHECKS is not set CONFIG_SCHED_AUTOGROUP=y @@ -1544,7 +1544,7 @@ CONFIG_I2C_ALGOBIT=y CONFIG_I2C_DESIGNWARE_CORE_FORK=y CONFIG_I2C_DESIGNWARE_PCI_FORK=y # CONFIG_I2C_DESIGNWARE_PLATFORM_FORK is not set -# CONFIG_I2C_DW_SPEED_MODE_DEBUG is not set +CONFIG_I2C_DW_SPEED_MODE_DEBUG=y CONFIG_I2C_PMIC=y # CONFIG_I2C_DESIGNWARE_PCI is not set # CONFIG_I2C_EG20T is not set @@ -1719,6 +1719,7 @@ CONFIG_BATTERY_MAX17042=y # CONFIG_POWER_RESET is not set # CONFIG_POWER_AVS is not set CONFIG_HWMON=y +CONFIG_INTEL_MCU=y # CONFIG_HWMON_VID is not set # CONFIG_HWMON_DEBUG_CHIP is not set @@ -2039,13 +2040,80 @@ CONFIG_VIDEO_V4L2_SUBDEV_API=y CONFIG_VIDEO_V4L2=y # CONFIG_VIDEO_ADV_DEBUG is not set # CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +CONFIG_VIDEOBUF2_CORE=m +CONFIG_VIDEOBUF2_MEMOPS=m +CONFIG_VIDEOBUF2_VMALLOC=m # CONFIG_VIDEO_V4L2_INT_DEVICE is not set # CONFIG_TTPCI_EEPROM is not set # # Media drivers # -# CONFIG_MEDIA_USB_SUPPORT is not set +CONFIG_MEDIA_USB_SUPPORT=y + +# +# Webcam devices +# +CONFIG_USB_VIDEO_CLASS=m +CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y +CONFIG_USB_GSPCA=m +# CONFIG_USB_M5602 is not set +# CONFIG_USB_STV06XX is not set +# CONFIG_USB_GL860 is not set +# CONFIG_USB_GSPCA_BENQ is not set +# CONFIG_USB_GSPCA_CONEX is not set +# CONFIG_USB_GSPCA_CPIA1 is not set +# CONFIG_USB_GSPCA_ETOMS is not set +# CONFIG_USB_GSPCA_FINEPIX is not set +# CONFIG_USB_GSPCA_JEILINJ is not set +# CONFIG_USB_GSPCA_JL2005BCD is not set +# CONFIG_USB_GSPCA_KINECT is not set +# CONFIG_USB_GSPCA_KONICA is not set +# CONFIG_USB_GSPCA_MARS is not set +# CONFIG_USB_GSPCA_MR97310A is not set +# CONFIG_USB_GSPCA_NW80X is not set +# CONFIG_USB_GSPCA_OV519 is not set +# CONFIG_USB_GSPCA_OV534 is not set +# CONFIG_USB_GSPCA_OV534_9 is not set +# CONFIG_USB_GSPCA_PAC207 is not set +# CONFIG_USB_GSPCA_PAC7302 is not set +# CONFIG_USB_GSPCA_PAC7311 is not set +# CONFIG_USB_GSPCA_SE401 is not set +# CONFIG_USB_GSPCA_SN9C2028 is not set +# CONFIG_USB_GSPCA_SN9C20X is not set +# CONFIG_USB_GSPCA_SONIXB is not set +# CONFIG_USB_GSPCA_SONIXJ is not set +# CONFIG_USB_GSPCA_SPCA500 is not set +# CONFIG_USB_GSPCA_SPCA501 is not set +# CONFIG_USB_GSPCA_SPCA505 is not set +# CONFIG_USB_GSPCA_SPCA506 is not set +# CONFIG_USB_GSPCA_SPCA508 is not set +# CONFIG_USB_GSPCA_SPCA561 is not set +# CONFIG_USB_GSPCA_SPCA1528 is not set +# CONFIG_USB_GSPCA_SQ905 is not set +# CONFIG_USB_GSPCA_SQ905C is not set +# CONFIG_USB_GSPCA_SQ930X is not set +# CONFIG_USB_GSPCA_STK014 is not set +# CONFIG_USB_GSPCA_STV0680 is not set +# CONFIG_USB_GSPCA_SUNPLUS is not set +# CONFIG_USB_GSPCA_T613 is not set +# CONFIG_USB_GSPCA_TOPRO is not set +# CONFIG_USB_GSPCA_TV8532 is not set +# CONFIG_USB_GSPCA_VC032X is not set +# CONFIG_USB_GSPCA_VICAM is not set +# CONFIG_USB_GSPCA_XIRLINK_CIT is not set +# CONFIG_USB_GSPCA_ZC3XX is not set +# CONFIG_USB_PWC is not set +# CONFIG_VIDEO_CPIA2 is not set +# CONFIG_USB_ZR364XX is not set +# CONFIG_USB_STKWEBCAM is not set +# CONFIG_USB_S2255 is not set +# CONFIG_USB_SN9C102 is not set + +# +# Webcam, TV (analog/digital) USB devices +# +# CONFIG_VIDEO_EM28XX is not set # CONFIG_MEDIA_PCI_SUPPORT is not set # CONFIG_V4L_PLATFORM_DRIVERS is not set # CONFIG_V4L_MEM2MEM_DRIVERS is not set @@ -2314,7 +2382,8 @@ CONFIG_SOUND=y CONFIG_SND=y CONFIG_SND_TIMER=y CONFIG_SND_PCM=y -CONFIG_SND_COMPRESS_OFFLOAD=y +# CONFIG_SND_COMPRESS_OFFLOAD is not set +# CONFIG_SND_EFFECTS_OFFLOAD is not set CONFIG_SND_JACK=y CONFIG_SND_SEQUENCER=y # CONFIG_SND_SEQ_DUMMY is not set @@ -2344,7 +2413,7 @@ CONFIG_SND_ALOOP=y # CONFIG_SND_PCI is not set # CONFIG_SND_SPI is not set CONFIG_SND_USB=y -# CONFIG_SND_USB_AUDIO is not set +CONFIG_SND_USB_AUDIO=y # CONFIG_SND_USB_UA101 is not set # CONFIG_SND_USB_USX2Y is not set # CONFIG_SND_USB_CAIAQ is not set @@ -2353,8 +2422,15 @@ CONFIG_SND_USB=y CONFIG_SND_SOC=y # CONFIG_SND_ATMEL_SOC is not set # CONFIG_SND_MFLD_MACHINE is not set +CONFIG_SND_INTEL_SST=y +CONFIG_SND_MRFLD_MACHINE=y +CONFIG_SND_SST_PLATFORM=y +CONFIG_SST_MRFLD_DPCM=y +CONFIG_SND_SST_MACHINE=y CONFIG_SND_SOC_I2C_AND_SPI=y # CONFIG_SND_SOC_ALL_CODECS is not set +CONFIG_SND_SOC_WM_HUBS=y +CONFIG_SND_SOC_WM8994=y # CONFIG_SND_SIMPLE_CARD is not set # CONFIG_SOUND_PRIME is not set @@ -2672,7 +2748,7 @@ CONFIG_USB_U_SERIAL=m # CONFIG_USB_G_ACM_MS is not set CONFIG_USB_G_MULTI=m CONFIG_USB_G_MULTI_RNDIS=y -# CONFIG_USB_G_MULTI_CDC is not set +CONFIG_USB_G_MULTI_CDC=y # CONFIG_USB_G_HID is not set # CONFIG_USB_G_DBGP is not set # CONFIG_USB_G_WEBCAM is not set @@ -3030,6 +3106,7 @@ CONFIG_INTEL_MID_POWER_BUTTON=y # CONFIG_IBM_RTL is not set # CONFIG_SAMSUNG_LAPTOP is not set CONFIG_INTEL_SCU_FLIS=y +CONFIG_INTEL_PSH_IPC=y # # Hardware Spinlock drivers @@ -3296,7 +3373,22 @@ CONFIG_PSTORE_RAM=y # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set # CONFIG_F2FS_FS is not set -# CONFIG_AUFS_FS is not set +CONFIG_AUFS_FS=m +CONFIG_AUFS_BRANCH_MAX_127=y +# CONFIG_AUFS_BRANCH_MAX_511 is not set +# CONFIG_AUFS_BRANCH_MAX_1023 is not set +# CONFIG_AUFS_BRANCH_MAX_32767 is not set +CONFIG_AUFS_SBILIST=y +# CONFIG_AUFS_HNOTIFY is not set +# CONFIG_AUFS_EXPORT is not set +# CONFIG_AUFS_RDU is not set +# CONFIG_AUFS_PROC_MAP is not set +# CONFIG_AUFS_SP_IATTR is not set +# CONFIG_AUFS_SHWH is not set +# CONFIG_AUFS_BR_RAMFS is not set +# CONFIG_AUFS_BR_FUSE is not set +CONFIG_AUFS_BDEV_LOOP=y +# CONFIG_AUFS_DEBUG is not set CONFIG_NETWORK_FILESYSTEMS=y CONFIG_NFS_FS=y CONFIG_NFS_V2=y diff --git a/device-software/meta-edison/recipes-kernel/linux/files/upstream_to_edison.patch b/device-software/meta-edison/recipes-kernel/linux/files/upstream_to_edison.patch index 6207981..14ed5ec 100644 --- a/device-software/meta-edison/recipes-kernel/linux/files/upstream_to_edison.patch +++ b/device-software/meta-edison/recipes-kernel/linux/files/upstream_to_edison.patch @@ -1,16 +1,16 @@ -From 225d200472d9c9af2117c8085b3682bcdd4dbeae Mon Sep 17 00:00:00 2001 +From 0146a06734d5329d58c801e99f1bef8b9c373036 Mon Sep 17 00:00:00 2001 From: Jenkins -Date: Tue, 14 Oct 2014 14:57:16 +0200 +Date: Fri, 30 Jan 2015 14:24:05 +0100 Subject: [PATCH] Squashed all commits from upstream to Edison -Change-Id: Ifec2f1375746c4c608cb0f799a6a5b9c2c96eaa6 +Change-Id: Id0e266dc44eb4f56c9e1970345eba7a1ff2b3baa --- Documentation/kernel-parameters.txt | 6 +- arch/x86/Kconfig | 62 +- arch/x86/Kconfig.cpu | 19 +- arch/x86/Makefile | 2 + arch/x86/Makefile_32.cpu | 2 + - arch/x86/configs/i386_edison_defconfig | 3768 +++++++++++++ + arch/x86/configs/i386_edison_defconfig | 3860 +++++++++++++ arch/x86/configs/i386_mrfl_defconfig | 3980 ++++++++++++++ arch/x86/include/asm/apb_timer.h | 1 + arch/x86/include/asm/bcm_bt_lpm.h | 46 + @@ -29,15 +29,21 @@ Change-Id: Ifec2f1375746c4c608cb0f799a6a5b9c2c96eaa6 arch/x86/include/asm/intel_mid_thermal.h | 77 + arch/x86/include/asm/intel_mid_vrtc.h | 9 + arch/x86/include/asm/intel_mip.h | 32 + + arch/x86/include/asm/intel_psh_ipc.h | 35 + arch/x86/include/asm/intel_scu_flis.h | 326 ++ arch/x86/include/asm/intel_scu_ipc.h | 105 +- arch/x86/include/asm/intel_scu_ipcutil.h | 135 + arch/x86/include/asm/intel_scu_pmic.h | 16 + arch/x86/include/asm/intel_soc_debug.h | 43 + + arch/x86/include/asm/intel_sst_mrfld.h | 44 + arch/x86/include/asm/module.h | 2 +- arch/x86/include/asm/mrst-vrtc.h | 9 - arch/x86/include/asm/mrst.h | 81 - arch/x86/include/asm/mwait.h | 6 + + arch/x86/include/asm/platform_byt_audio.h | 49 + + arch/x86/include/asm/platform_mrfld_audio.h | 25 + + arch/x86/include/asm/platform_sst.h | 132 + + arch/x86/include/asm/platform_sst_audio.h | 161 + arch/x86/include/asm/pmic_pdata.h | 45 + arch/x86/include/asm/required-features.h | 2 +- arch/x86/include/asm/scu_ipc_rpmsg.h | 19 + @@ -58,11 +64,12 @@ Change-Id: Ifec2f1375746c4c608cb0f799a6a5b9c2c96eaa6 arch/x86/pci/mrst.c | 21 +- arch/x86/platform/Makefile | 2 +- arch/x86/platform/intel-mid/Makefile | 27 + - arch/x86/platform/intel-mid/board.c | 160 + - arch/x86/platform/intel-mid/device_libs/Makefile | 64 + + arch/x86/platform/intel-mid/board.c | 166 + + arch/x86/platform/intel-mid/device_libs/Makefile | 66 + .../platform/intel-mid/device_libs/pci/Makefile | 6 + .../intel-mid/device_libs/pci/platform_sdhci_pci.c | 588 ++ .../intel-mid/device_libs/pci/platform_sdhci_pci.h | 30 + + .../intel-mid/device_libs/pci/platform_sst_pci.c | 229 + .../intel-mid/device_libs/pci/platform_usb_otg.c | 93 + .../intel-mid/device_libs/platform_ads7955.c | 37 + .../intel-mid/device_libs/platform_ads7955.h | 20 + @@ -99,6 +106,7 @@ Change-Id: Ifec2f1375746c4c608cb0f799a6a5b9c2c96eaa6 .../device_libs/platform_mrfl_regulator.c | 125 + .../intel-mid/device_libs/platform_mrfl_thermal.c | 133 + .../intel-mid/device_libs/platform_mrfl_thermal.h | 26 + + .../intel-mid/device_libs/platform_mrfld_audio.c | 130 + .../platform/intel-mid/device_libs/platform_msic.c | 92 + .../platform/intel-mid/device_libs/platform_msic.h | 21 + .../intel-mid/device_libs/platform_msic_adc.c | 56 + @@ -126,6 +134,7 @@ Change-Id: Ifec2f1375746c4c608cb0f799a6a5b9c2c96eaa6 .../intel-mid/device_libs/platform_soc_thermal.h | 20 + .../intel-mid/device_libs/platform_spidev.c | 73 + .../intel-mid/device_libs/platform_spidev.h | 20 + + .../intel-mid/device_libs/platform_sst_audio.c | 152 + .../intel-mid/device_libs/platform_tc35876x.c | 26 + .../intel-mid/device_libs/platform_tc35876x.h | 16 + .../intel-mid/device_libs/platform_tca6416.c | 45 + @@ -134,12 +143,14 @@ Change-Id: Ifec2f1375746c4c608cb0f799a6a5b9c2c96eaa6 .../platform/intel-mid/device_libs/platform_wifi.h | 21 + .../intel-mid/device_libs/platform_wl12xx.c | 182 + .../intel-mid/device_libs/platform_wl12xx.h | 21 + + .../intel-mid/device_libs/platform_wm8994.c | 221 + + .../intel-mid/device_libs/platform_wm8994.h | 5 + .../platform/intel-mid/early_printk_intel_mid.c | 637 +++ arch/x86/platform/intel-mid/intel-mid.c | 239 + arch/x86/platform/intel-mid/intel_mid_pcihelpers.c | 105 + arch/x86/platform/intel-mid/intel_mid_scu.c | 92 + arch/x86/platform/intel-mid/intel_mid_scu.h | 15 + - arch/x86/platform/intel-mid/intel_mid_sfi.c | 588 ++ + arch/x86/platform/intel-mid/intel_mid_sfi.c | 608 ++ arch/x86/platform/intel-mid/intel_mid_vrtc.c | 169 + arch/x86/platform/intel-mid/intel_mid_weak_decls.h | 22 + arch/x86/platform/intel-mid/intel_soc_clv.c | 146 + @@ -172,9 +183,9 @@ Change-Id: Ifec2f1375746c4c608cb0f799a6a5b9c2c96eaa6 drivers/cpufreq/sfi-cpufreq.c | 581 ++ drivers/cpufreq/sfi-cpufreq.h | 65 + drivers/dma/Makefile | 2 +- - drivers/dma/intel_mid_dma.c | 1278 +++-- - drivers/dma/intel_mid_dma_acpi.c | 177 + - drivers/dma/intel_mid_dma_regs.h | 164 +- + drivers/dma/intel_mid_dma.c | 1280 +++-- + drivers/dma/intel_mid_dma_acpi.c | 192 + + drivers/dma/intel_mid_dma_regs.h | 168 +- drivers/gpio/Kconfig | 11 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-langwell.c | 1061 +++- @@ -185,9 +196,11 @@ Change-Id: Ifec2f1375746c4c608cb0f799a6a5b9c2c96eaa6 drivers/gpu/drm/gma500/mdfld_dsi_output.h | 2 +- drivers/gpu/drm/gma500/oaktrail_device.c | 2 +- drivers/gpu/drm/gma500/oaktrail_lvds.c | 2 +- - drivers/hwmon/Kconfig | 16 + - drivers/hwmon/Makefile | 1 + + drivers/hwmon/Kconfig | 25 + + drivers/hwmon/Makefile | 2 + drivers/hwmon/coretemp.c | 362 +- + drivers/hwmon/intel_mcu_common.c | 700 +++ + drivers/hwmon/intel_mcu_common.h | 79 + drivers/hwmon/intel_mid_gpadc.c | 1212 ++++ drivers/i2c/busses/Kconfig | 46 + drivers/i2c/busses/Makefile | 8 + @@ -215,30 +228,31 @@ Change-Id: Ifec2f1375746c4c608cb0f799a6a5b9c2c96eaa6 drivers/misc/pti.c | 95 +- drivers/misc/stm.c | 470 ++ drivers/misc/stm.h | 114 + - drivers/mmc/card/block.c | 6 + + drivers/mmc/card/block.c | 194 +- drivers/mmc/card/queue.c | 3 +- drivers/mmc/card/queue.h | 1 + drivers/mmc/core/Makefile | 2 +- drivers/mmc/core/bus.c | 3 +- drivers/mmc/core/core.c | 25 +- drivers/mmc/core/debugfs.c | 3 + - drivers/mmc/core/mmc.c | 161 +- - drivers/mmc/core/mmc_ops.c | 4 +- + drivers/mmc/core/mmc.c | 177 +- + drivers/mmc/core/mmc_ops.c | 416 +- drivers/mmc/core/mmc_panic_ops.c | 837 +++ drivers/mmc/core/sdio_cis.c | 129 +- drivers/mmc/host/sdhci-pci.c | 819 ++- - drivers/mmc/host/sdhci.c | 1452 ++++- + drivers/mmc/host/sdhci.c | 1454 ++++- drivers/mmc/host/sdhci.h | 13 +- drivers/net/wireless/Kconfig | 10 + drivers/pci/Makefile | 1 + drivers/pci/pci-atom_soc.c | 78 + drivers/pci/pci.c | 5 +- drivers/pci/quirks.c | 6 +- - drivers/platform/x86/Kconfig | 23 + - drivers/platform/x86/Makefile | 4 +- + drivers/platform/x86/Kconfig | 40 + + drivers/platform/x86/Makefile | 6 +- drivers/platform/x86/intel_mid_powerbtn.c | 252 +- drivers/platform/x86/intel_mid_thermal.c | 574 -- drivers/platform/x86/intel_pmic_gpio.c | 2 +- + drivers/platform/x86/intel_psh_ipc.c | 637 +++ drivers/platform/x86/intel_scu_flis.c | 747 +++ drivers/platform/x86/intel_scu_fw_update.c | 1087 ++++ drivers/platform/x86/intel_scu_ipc.c | 636 +-- @@ -260,7 +274,7 @@ Change-Id: Ifec2f1375746c4c608cb0f799a6a5b9c2c96eaa6 drivers/pwm/Kconfig | 13 + drivers/pwm/Makefile | 2 + drivers/pwm/core.c | 25 +- - drivers/pwm/pwm-intel-mid.c | 405 ++ + drivers/pwm/pwm-intel-mid.c | 404 ++ drivers/pwm/sysfs.c | 353 ++ drivers/regulator/Kconfig | 6 +- drivers/regulator/Makefile | 2 + @@ -269,7 +283,7 @@ Change-Id: Ifec2f1375746c4c608cb0f799a6a5b9c2c96eaa6 drivers/remoteproc/Makefile | 1 + drivers/remoteproc/intel_mid_rproc_core.c | 269 + drivers/remoteproc/intel_mid_rproc_core.h | 82 + - drivers/remoteproc/intel_mid_rproc_scu.c | 438 ++ + drivers/remoteproc/intel_mid_rproc_scu.c | 441 ++ drivers/rpmsg/Kconfig | 9 + drivers/rpmsg/Makefile | 1 + drivers/rpmsg/intel_mid_rpmsg.c | 446 ++ @@ -277,7 +291,7 @@ Change-Id: Ifec2f1375746c4c608cb0f799a6a5b9c2c96eaa6 drivers/rtc/rtc-mrst.c | 296 +- drivers/spi/Kconfig | 9 + drivers/spi/Makefile | 1 + - drivers/spi/intel_mid_ssp_spi.c | 1751 ++++++ + drivers/spi/intel_mid_ssp_spi.c | 1787 ++++++ drivers/spi/spi-dw-mid.c | 133 +- drivers/spi/spi-dw-pci.c | 102 +- drivers/spi/spi-dw.c | 151 +- @@ -346,15 +360,17 @@ Change-Id: Ifec2f1375746c4c608cb0f799a6a5b9c2c96eaa6 include/linux/irqdesc.h | 7 + include/linux/lnw_gpio.h | 14 + include/linux/mfd/intel_msic.h | 42 + - include/linux/mmc/card.h | 5 + - include/linux/mmc/core.h | 1 + + include/linux/mfd/wm8994/pdata.h | 19 + + include/linux/mfd/wm8994/registers.h | 8 + + include/linux/mmc/card.h | 11 + + include/linux/mmc/core.h | 35 + include/linux/mmc/host.h | 64 + include/linux/mmc/mmc.h | 5 + include/linux/mmc/sdhci-pci-data.h | 16 + include/linux/mmc/sdhci.h | 52 + include/linux/mmc/sdio_ids.h | 2 + include/linux/panic_gbuffer.h | 37 + - include/linux/pci_ids.h | 13 + + include/linux/pci_ids.h | 14 + include/linux/platform_data/intel_mid_remoteproc.h | 119 + include/linux/platform_data/ti-ads7955.h | 21 + include/linux/power/battery_id.h | 78 + @@ -380,7 +396,21 @@ Change-Id: Ifec2f1375746c4c608cb0f799a6a5b9c2c96eaa6 include/linux/usb/phy.h | 14 + include/linux/wl12xx.h | 5 + include/linux/wlan_plat.h | 29 + + include/net/bluetooth/hci_core.h | 2 + + include/net/bluetooth/l2cap.h | 8 +- + include/sound/compress_driver.h | 15 + + include/sound/core.h | 5 + + include/sound/effect_driver.h | 63 + + include/sound/intel_sst_ioctl.h | 63 + + include/sound/pcm.h | 1 + + include/sound/soc-dai.h | 4 +- + include/sound/soc-dapm.h | 2 + + include/sound/soc-dpcm.h | 22 + + include/sound/soc.h | 35 + + include/uapi/linux/mmc/ioctl.h | 13 + include/uapi/linux/usb/ch9.h | 1 + + include/uapi/sound/compress_offload.h | 8 +- + include/uapi/sound/effect_offload.h | 62 + init/main.c | 2 + kernel/cgroup.c | 6 +- kernel/irq/chip.c | 1 + @@ -388,8 +418,66 @@ Change-Id: Ifec2f1375746c4c608cb0f799a6a5b9c2c96eaa6 kernel/power/process.c | 2 + kernel/printk.c | 18 +- kernel/trace/trace.c | 5 +- + net/bluetooth/l2cap_core.c | 20 +- + net/bluetooth/l2cap_sock.c | 5 +- + sound/core/Kconfig | 3 + + sound/core/Makefile | 2 + + sound/core/compress_offload.c | 123 +- + sound/core/effects_offload.c | 307 ++ + sound/soc/Kconfig | 2 +- + sound/soc/Makefile | 2 +- sound/soc/codecs/sn95031.c | 2 +- - 384 files changed, 83928 insertions(+), 6468 deletions(-) + sound/soc/codecs/wm8994.c | 491 +- + sound/soc/codecs/wm8994.h | 14 + + sound/soc/codecs/wm_hubs.c | 20 +- + sound/soc/intel/Kconfig | 58 + + sound/soc/intel/Makefile | 22 + + sound/soc/intel/board/Makefile | 14 + + sound/soc/intel/board/merr_dpcm_dummy.c | 233 + + sound/soc/intel/board/merr_dpcm_wm8958.c | 932 ++++ + sound/soc/intel/board/merr_saltbay_wm8958.c | 887 +++ + sound/soc/intel/compress.c | 254 + + sound/soc/intel/effects.c | 405 ++ + sound/soc/intel/pcm.c | 1021 ++++ + sound/soc/intel/platform-libs/controls_v1.c | 184 + + sound/soc/intel/platform-libs/controls_v2.c | 1770 ++++++ + sound/soc/intel/platform-libs/controls_v2.h | 708 +++ + sound/soc/intel/platform-libs/controls_v2_dpcm.c | 1803 ++++++ + sound/soc/intel/platform-libs/ipc_lib.h | 33 + + sound/soc/intel/platform-libs/ipc_lib_v2.c | 109 + + sound/soc/intel/platform-libs/sst_widgets.h | 337 ++ + sound/soc/intel/platform_ipc_v2.h | 693 +++ + sound/soc/intel/ssp/Makefile | 5 + + sound/soc/intel/ssp/mid_ssp.c | 1436 +++++ + sound/soc/intel/ssp/mid_ssp.h | 124 + + sound/soc/intel/sst/Makefile | 13 + + sound/soc/intel/sst/sst.c | 1255 +++++ + sound/soc/intel/sst/sst.h | 932 ++++ + sound/soc/intel/sst/sst_acpi.c | 669 +++ + sound/soc/intel/sst/sst_app_compat_interface.c | 85 + + sound/soc/intel/sst/sst_app_interface.c | 342 ++ + sound/soc/intel/sst/sst_debug.c | 1328 +++++ + sound/soc/intel/sst/sst_drv_interface.c | 1111 ++++ + sound/soc/intel/sst/sst_dsp.c | 1995 +++++++ + sound/soc/intel/sst/sst_ipc.c | 768 +++ + sound/soc/intel/sst/sst_pvt.c | 577 ++ + sound/soc/intel/sst/sst_stream.c | 831 +++ + sound/soc/intel/sst/sst_trace.h | 147 + + sound/soc/intel/sst_platform.h | 155 + + sound/soc/intel/sst_platform_pvt.h | 127 + + sound/soc/mid-x86/Kconfig | 13 - + sound/soc/mid-x86/Makefile | 5 - + sound/soc/mid-x86/mfld_machine.c | 447 -- + sound/soc/mid-x86/sst_dsp.h | 134 - + sound/soc/mid-x86/sst_platform.c | 733 --- + sound/soc/mid-x86/sst_platform.h | 157 - + sound/soc/soc-compress.c | 343 +- + sound/soc/soc-core.c | 36 +- + sound/soc/soc-dapm.c | 13 +- + sound/soc/soc-jack.c | 14 +- + sound/soc/soc-pcm.c | 83 +- + sound/soc/soc-utils.c | 34 +- + 472 files changed, 110390 insertions(+), 8166 deletions(-) create mode 100644 arch/x86/configs/i386_edison_defconfig create mode 100644 arch/x86/configs/i386_mrfl_defconfig create mode 100644 arch/x86/include/asm/bcm_bt_lpm.h @@ -406,12 +494,18 @@ Change-Id: Ifec2f1375746c4c608cb0f799a6a5b9c2c96eaa6 create mode 100644 arch/x86/include/asm/intel_mid_thermal.h create mode 100644 arch/x86/include/asm/intel_mid_vrtc.h create mode 100644 arch/x86/include/asm/intel_mip.h + create mode 100644 arch/x86/include/asm/intel_psh_ipc.h create mode 100644 arch/x86/include/asm/intel_scu_flis.h create mode 100644 arch/x86/include/asm/intel_scu_ipcutil.h create mode 100644 arch/x86/include/asm/intel_scu_pmic.h create mode 100644 arch/x86/include/asm/intel_soc_debug.h + create mode 100644 arch/x86/include/asm/intel_sst_mrfld.h delete mode 100644 arch/x86/include/asm/mrst-vrtc.h delete mode 100644 arch/x86/include/asm/mrst.h + create mode 100644 arch/x86/include/asm/platform_byt_audio.h + create mode 100644 arch/x86/include/asm/platform_mrfld_audio.h + create mode 100644 arch/x86/include/asm/platform_sst.h + create mode 100644 arch/x86/include/asm/platform_sst_audio.h create mode 100644 arch/x86/include/asm/pmic_pdata.h create mode 100644 arch/x86/include/asm/scu_ipc_rpmsg.h create mode 100644 arch/x86/platform/intel-mid/Makefile @@ -420,6 +514,7 @@ Change-Id: Ifec2f1375746c4c608cb0f799a6a5b9c2c96eaa6 create mode 100644 arch/x86/platform/intel-mid/device_libs/pci/Makefile create mode 100644 arch/x86/platform/intel-mid/device_libs/pci/platform_sdhci_pci.c create mode 100644 arch/x86/platform/intel-mid/device_libs/pci/platform_sdhci_pci.h + create mode 100644 arch/x86/platform/intel-mid/device_libs/pci/platform_sst_pci.c create mode 100644 arch/x86/platform/intel-mid/device_libs/pci/platform_usb_otg.c create mode 100644 arch/x86/platform/intel-mid/device_libs/platform_ads7955.c create mode 100644 arch/x86/platform/intel-mid/device_libs/platform_ads7955.h @@ -456,6 +551,7 @@ Change-Id: Ifec2f1375746c4c608cb0f799a6a5b9c2c96eaa6 create mode 100644 arch/x86/platform/intel-mid/device_libs/platform_mrfl_regulator.c create mode 100644 arch/x86/platform/intel-mid/device_libs/platform_mrfl_thermal.c create mode 100644 arch/x86/platform/intel-mid/device_libs/platform_mrfl_thermal.h + create mode 100644 arch/x86/platform/intel-mid/device_libs/platform_mrfld_audio.c create mode 100644 arch/x86/platform/intel-mid/device_libs/platform_msic.c create mode 100644 arch/x86/platform/intel-mid/device_libs/platform_msic.h create mode 100644 arch/x86/platform/intel-mid/device_libs/platform_msic_adc.c @@ -483,6 +579,7 @@ Change-Id: Ifec2f1375746c4c608cb0f799a6a5b9c2c96eaa6 create mode 100644 arch/x86/platform/intel-mid/device_libs/platform_soc_thermal.h create mode 100644 arch/x86/platform/intel-mid/device_libs/platform_spidev.c create mode 100644 arch/x86/platform/intel-mid/device_libs/platform_spidev.h + create mode 100644 arch/x86/platform/intel-mid/device_libs/platform_sst_audio.c create mode 100644 arch/x86/platform/intel-mid/device_libs/platform_tc35876x.c create mode 100644 arch/x86/platform/intel-mid/device_libs/platform_tc35876x.h create mode 100644 arch/x86/platform/intel-mid/device_libs/platform_tca6416.c @@ -491,6 +588,8 @@ Change-Id: Ifec2f1375746c4c608cb0f799a6a5b9c2c96eaa6 create mode 100644 arch/x86/platform/intel-mid/device_libs/platform_wifi.h create mode 100644 arch/x86/platform/intel-mid/device_libs/platform_wl12xx.c create mode 100644 arch/x86/platform/intel-mid/device_libs/platform_wl12xx.h + create mode 100644 arch/x86/platform/intel-mid/device_libs/platform_wm8994.c + create mode 100644 arch/x86/platform/intel-mid/device_libs/platform_wm8994.h create mode 100644 arch/x86/platform/intel-mid/early_printk_intel_mid.c create mode 100644 arch/x86/platform/intel-mid/intel-mid.c create mode 100644 arch/x86/platform/intel-mid/intel_mid_pcihelpers.c @@ -525,6 +624,8 @@ Change-Id: Ifec2f1375746c4c608cb0f799a6a5b9c2c96eaa6 create mode 100644 drivers/dma/intel_mid_dma_acpi.c create mode 100644 drivers/gpio/gpiodebug.c create mode 100644 drivers/gpio/gpiodebug.h + create mode 100644 drivers/hwmon/intel_mcu_common.c + create mode 100644 drivers/hwmon/intel_mcu_common.h create mode 100644 drivers/hwmon/intel_mid_gpadc.c create mode 100644 drivers/i2c/busses/i2c-pmic-regs.h create mode 100644 drivers/i2c/busses/i2c-pmic.c @@ -540,6 +641,7 @@ Change-Id: Ifec2f1375746c4c608cb0f799a6a5b9c2c96eaa6 create mode 100644 drivers/mmc/core/mmc_panic_ops.c create mode 100644 drivers/pci/pci-atom_soc.c delete mode 100644 drivers/platform/x86/intel_mid_thermal.c + create mode 100644 drivers/platform/x86/intel_psh_ipc.c create mode 100644 drivers/platform/x86/intel_scu_flis.c create mode 100644 drivers/platform/x86/intel_scu_fw_update.c create mode 100644 drivers/platform/x86/intel_scu_mip.c @@ -600,6 +702,51 @@ Change-Id: Ifec2f1375746c4c608cb0f799a6a5b9c2c96eaa6 create mode 100644 include/linux/usb/dwc3-intel-mrfl.h create mode 100644 include/linux/usb/penwell_otg.h create mode 100644 include/linux/wlan_plat.h + create mode 100644 include/sound/effect_driver.h + create mode 100644 include/sound/intel_sst_ioctl.h + create mode 100644 include/uapi/sound/effect_offload.h + create mode 100644 sound/core/effects_offload.c + create mode 100644 sound/soc/intel/Kconfig + create mode 100644 sound/soc/intel/Makefile + create mode 100644 sound/soc/intel/board/Makefile + create mode 100644 sound/soc/intel/board/merr_dpcm_dummy.c + create mode 100644 sound/soc/intel/board/merr_dpcm_wm8958.c + create mode 100644 sound/soc/intel/board/merr_saltbay_wm8958.c + create mode 100644 sound/soc/intel/compress.c + create mode 100644 sound/soc/intel/effects.c + create mode 100644 sound/soc/intel/pcm.c + create mode 100644 sound/soc/intel/platform-libs/controls_v1.c + create mode 100644 sound/soc/intel/platform-libs/controls_v2.c + create mode 100644 sound/soc/intel/platform-libs/controls_v2.h + create mode 100644 sound/soc/intel/platform-libs/controls_v2_dpcm.c + create mode 100644 sound/soc/intel/platform-libs/ipc_lib.h + create mode 100644 sound/soc/intel/platform-libs/ipc_lib_v2.c + create mode 100644 sound/soc/intel/platform-libs/sst_widgets.h + create mode 100644 sound/soc/intel/platform_ipc_v2.h + create mode 100644 sound/soc/intel/ssp/Makefile + create mode 100644 sound/soc/intel/ssp/mid_ssp.c + create mode 100644 sound/soc/intel/ssp/mid_ssp.h + create mode 100644 sound/soc/intel/sst/Makefile + create mode 100644 sound/soc/intel/sst/sst.c + create mode 100644 sound/soc/intel/sst/sst.h + create mode 100644 sound/soc/intel/sst/sst_acpi.c + create mode 100644 sound/soc/intel/sst/sst_app_compat_interface.c + create mode 100644 sound/soc/intel/sst/sst_app_interface.c + create mode 100644 sound/soc/intel/sst/sst_debug.c + create mode 100644 sound/soc/intel/sst/sst_drv_interface.c + create mode 100644 sound/soc/intel/sst/sst_dsp.c + create mode 100644 sound/soc/intel/sst/sst_ipc.c + create mode 100644 sound/soc/intel/sst/sst_pvt.c + create mode 100644 sound/soc/intel/sst/sst_stream.c + create mode 100644 sound/soc/intel/sst/sst_trace.h + create mode 100644 sound/soc/intel/sst_platform.h + create mode 100644 sound/soc/intel/sst_platform_pvt.h + delete mode 100644 sound/soc/mid-x86/Kconfig + delete mode 100644 sound/soc/mid-x86/Makefile + delete mode 100644 sound/soc/mid-x86/mfld_machine.c + delete mode 100644 sound/soc/mid-x86/sst_dsp.h + delete mode 100644 sound/soc/mid-x86/sst_platform.c + delete mode 100644 sound/soc/mid-x86/sst_platform.h diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 2fe6e76..8ec1e55 100644 @@ -802,10 +949,10 @@ index 6647ed4..a2eb6c6 100644 cflags-$(CONFIG_MELAN) += -march=i486 diff --git a/arch/x86/configs/i386_edison_defconfig b/arch/x86/configs/i386_edison_defconfig new file mode 100644 -index 0000000..e79bdd1 +index 0000000..b1ed708 --- /dev/null +++ b/arch/x86/configs/i386_edison_defconfig -@@ -0,0 +1,3768 @@ +@@ -0,0 +1,3860 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/x86 3.10.17 Kernel Configuration @@ -961,7 +1108,7 @@ index 0000000..e79bdd1 +# CONFIG_IPC_NS is not set +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set -+# CONFIG_NET_NS is not set ++CONFIG_NET_NS=y +CONFIG_UIDGID_CONVERTED=y +# CONFIG_UIDGID_STRICT_TYPE_CHECKS is not set +CONFIG_SCHED_AUTOGROUP=y @@ -2352,7 +2499,7 @@ index 0000000..e79bdd1 +CONFIG_I2C_DESIGNWARE_CORE_FORK=y +CONFIG_I2C_DESIGNWARE_PCI_FORK=y +# CONFIG_I2C_DESIGNWARE_PLATFORM_FORK is not set -+# CONFIG_I2C_DW_SPEED_MODE_DEBUG is not set ++CONFIG_I2C_DW_SPEED_MODE_DEBUG=y +CONFIG_I2C_PMIC=y +# CONFIG_I2C_DESIGNWARE_PCI is not set +# CONFIG_I2C_EG20T is not set @@ -2527,6 +2674,7 @@ index 0000000..e79bdd1 +# CONFIG_POWER_RESET is not set +# CONFIG_POWER_AVS is not set +CONFIG_HWMON=y ++CONFIG_INTEL_MCU=y +# CONFIG_HWMON_VID is not set +# CONFIG_HWMON_DEBUG_CHIP is not set + @@ -2847,13 +2995,80 @@ index 0000000..e79bdd1 +CONFIG_VIDEO_V4L2=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set ++CONFIG_VIDEOBUF2_CORE=m ++CONFIG_VIDEOBUF2_MEMOPS=m ++CONFIG_VIDEOBUF2_VMALLOC=m +# CONFIG_VIDEO_V4L2_INT_DEVICE is not set +# CONFIG_TTPCI_EEPROM is not set + +# +# Media drivers +# -+# CONFIG_MEDIA_USB_SUPPORT is not set ++CONFIG_MEDIA_USB_SUPPORT=y ++ ++# ++# Webcam devices ++# ++CONFIG_USB_VIDEO_CLASS=m ++CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y ++CONFIG_USB_GSPCA=m ++# CONFIG_USB_M5602 is not set ++# CONFIG_USB_STV06XX is not set ++# CONFIG_USB_GL860 is not set ++# CONFIG_USB_GSPCA_BENQ is not set ++# CONFIG_USB_GSPCA_CONEX is not set ++# CONFIG_USB_GSPCA_CPIA1 is not set ++# CONFIG_USB_GSPCA_ETOMS is not set ++# CONFIG_USB_GSPCA_FINEPIX is not set ++# CONFIG_USB_GSPCA_JEILINJ is not set ++# CONFIG_USB_GSPCA_JL2005BCD is not set ++# CONFIG_USB_GSPCA_KINECT is not set ++# CONFIG_USB_GSPCA_KONICA is not set ++# CONFIG_USB_GSPCA_MARS is not set ++# CONFIG_USB_GSPCA_MR97310A is not set ++# CONFIG_USB_GSPCA_NW80X is not set ++# CONFIG_USB_GSPCA_OV519 is not set ++# CONFIG_USB_GSPCA_OV534 is not set ++# CONFIG_USB_GSPCA_OV534_9 is not set ++# CONFIG_USB_GSPCA_PAC207 is not set ++# CONFIG_USB_GSPCA_PAC7302 is not set ++# CONFIG_USB_GSPCA_PAC7311 is not set ++# CONFIG_USB_GSPCA_SE401 is not set ++# CONFIG_USB_GSPCA_SN9C2028 is not set ++# CONFIG_USB_GSPCA_SN9C20X is not set ++# CONFIG_USB_GSPCA_SONIXB is not set ++# CONFIG_USB_GSPCA_SONIXJ is not set ++# CONFIG_USB_GSPCA_SPCA500 is not set ++# CONFIG_USB_GSPCA_SPCA501 is not set ++# CONFIG_USB_GSPCA_SPCA505 is not set ++# CONFIG_USB_GSPCA_SPCA506 is not set ++# CONFIG_USB_GSPCA_SPCA508 is not set ++# CONFIG_USB_GSPCA_SPCA561 is not set ++# CONFIG_USB_GSPCA_SPCA1528 is not set ++# CONFIG_USB_GSPCA_SQ905 is not set ++# CONFIG_USB_GSPCA_SQ905C is not set ++# CONFIG_USB_GSPCA_SQ930X is not set ++# CONFIG_USB_GSPCA_STK014 is not set ++# CONFIG_USB_GSPCA_STV0680 is not set ++# CONFIG_USB_GSPCA_SUNPLUS is not set ++# CONFIG_USB_GSPCA_T613 is not set ++# CONFIG_USB_GSPCA_TOPRO is not set ++# CONFIG_USB_GSPCA_TV8532 is not set ++# CONFIG_USB_GSPCA_VC032X is not set ++# CONFIG_USB_GSPCA_VICAM is not set ++# CONFIG_USB_GSPCA_XIRLINK_CIT is not set ++# CONFIG_USB_GSPCA_ZC3XX is not set ++# CONFIG_USB_PWC is not set ++# CONFIG_VIDEO_CPIA2 is not set ++# CONFIG_USB_ZR364XX is not set ++# CONFIG_USB_STKWEBCAM is not set ++# CONFIG_USB_S2255 is not set ++# CONFIG_USB_SN9C102 is not set ++ ++# ++# Webcam, TV (analog/digital) USB devices ++# ++# CONFIG_VIDEO_EM28XX is not set +# CONFIG_MEDIA_PCI_SUPPORT is not set +# CONFIG_V4L_PLATFORM_DRIVERS is not set +# CONFIG_V4L_MEM2MEM_DRIVERS is not set @@ -3122,7 +3337,8 @@ index 0000000..e79bdd1 +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y -+CONFIG_SND_COMPRESS_OFFLOAD=y ++# CONFIG_SND_COMPRESS_OFFLOAD is not set ++# CONFIG_SND_EFFECTS_OFFLOAD is not set +CONFIG_SND_JACK=y +CONFIG_SND_SEQUENCER=y +# CONFIG_SND_SEQ_DUMMY is not set @@ -3152,7 +3368,7 @@ index 0000000..e79bdd1 +# CONFIG_SND_PCI is not set +# CONFIG_SND_SPI is not set +CONFIG_SND_USB=y -+# CONFIG_SND_USB_AUDIO is not set ++CONFIG_SND_USB_AUDIO=y +# CONFIG_SND_USB_UA101 is not set +# CONFIG_SND_USB_USX2Y is not set +# CONFIG_SND_USB_CAIAQ is not set @@ -3161,8 +3377,15 @@ index 0000000..e79bdd1 +CONFIG_SND_SOC=y +# CONFIG_SND_ATMEL_SOC is not set +# CONFIG_SND_MFLD_MACHINE is not set ++CONFIG_SND_INTEL_SST=y ++CONFIG_SND_MRFLD_MACHINE=y ++CONFIG_SND_SST_PLATFORM=y ++CONFIG_SST_MRFLD_DPCM=y ++CONFIG_SND_SST_MACHINE=y +CONFIG_SND_SOC_I2C_AND_SPI=y +# CONFIG_SND_SOC_ALL_CODECS is not set ++CONFIG_SND_SOC_WM_HUBS=y ++CONFIG_SND_SOC_WM8994=y +# CONFIG_SND_SIMPLE_CARD is not set +# CONFIG_SOUND_PRIME is not set + @@ -3838,6 +4061,7 @@ index 0000000..e79bdd1 +# CONFIG_IBM_RTL is not set +# CONFIG_SAMSUNG_LAPTOP is not set +CONFIG_INTEL_SCU_FLIS=y ++CONFIG_INTEL_PSH_IPC=y + +# +# Hardware Spinlock drivers @@ -4104,7 +4328,22 @@ index 0000000..e79bdd1 +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +# CONFIG_F2FS_FS is not set -+# CONFIG_AUFS_FS is not set ++CONFIG_AUFS_FS=m ++CONFIG_AUFS_BRANCH_MAX_127=y ++# CONFIG_AUFS_BRANCH_MAX_511 is not set ++# CONFIG_AUFS_BRANCH_MAX_1023 is not set ++# CONFIG_AUFS_BRANCH_MAX_32767 is not set ++CONFIG_AUFS_SBILIST=y ++# CONFIG_AUFS_HNOTIFY is not set ++# CONFIG_AUFS_EXPORT is not set ++# CONFIG_AUFS_RDU is not set ++# CONFIG_AUFS_PROC_MAP is not set ++# CONFIG_AUFS_SP_IATTR is not set ++# CONFIG_AUFS_SHWH is not set ++# CONFIG_AUFS_BR_RAMFS is not set ++# CONFIG_AUFS_BR_FUSE is not set ++CONFIG_AUFS_BDEV_LOOP=y ++# CONFIG_AUFS_DEBUG is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V2=y @@ -9685,6 +9924,47 @@ index 0000000..f05fc05 + +int get_smip_property_by_name(enum platform_prop); +#endif +diff --git a/arch/x86/include/asm/intel_psh_ipc.h b/arch/x86/include/asm/intel_psh_ipc.h +new file mode 100644 +index 0000000..ed4d3c14 +--- /dev/null ++++ b/arch/x86/include/asm/intel_psh_ipc.h +@@ -0,0 +1,35 @@ ++#ifndef _ASM_X86_INTEL_PSH_IPC_H_ ++#define _ASM_X86_INTEL_PSH_IPC_H_ ++ ++#define CHANNEL_BUSY (1 << 31) ++#define PSH_IPC_CONTINUE (1 << 30) ++ ++struct psh_msg { ++ u32 msg; ++ u32 param; ++}; ++ ++enum psh_channel { ++ PSH_SEND_CH0 = 0, ++ PSH_SEND_CH1, ++ PSH_SEND_CH2, ++ PSH_SEND_CH3, ++ NUM_IA2PSH_IPC, ++ PSH_RECV_CH0 = NUM_IA2PSH_IPC, ++ PSH_RECV_CH1, ++ PSH_RECV_CH2, ++ PSH_RECV_CH3, ++ PSH_RECV_END, ++ NUM_PSH2IA_IPC = PSH_RECV_END - PSH_RECV_CH0, ++ NUM_ALL_CH = NUM_IA2PSH_IPC + NUM_PSH2IA_IPC, ++}; ++ ++typedef void(*psh_channel_handle_t)(u32 msg, u32 param, void *data); ++int intel_ia2psh_command(struct psh_msg *in, struct psh_msg *out, ++ int ch, int timeout); ++int intel_psh_ipc_bind(int ch, psh_channel_handle_t handle, void *data); ++void intel_psh_ipc_unbind(int ch); ++ ++void intel_psh_ipc_disable_irq(void); ++void intel_psh_ipc_enable_irq(void); ++#endif diff --git a/arch/x86/include/asm/intel_scu_flis.h b/arch/x86/include/asm/intel_scu_flis.h new file mode 100644 index 0000000..7cea735 @@ -10357,6 +10637,56 @@ index 0000000..9edb166 +#endif + +#endif +diff --git a/arch/x86/include/asm/intel_sst_mrfld.h b/arch/x86/include/asm/intel_sst_mrfld.h +new file mode 100644 +index 0000000..041ff85 +--- /dev/null ++++ b/arch/x86/include/asm/intel_sst_mrfld.h +@@ -0,0 +1,44 @@ ++/* intel_sst_mrlfd.h - Common enum of the Merrifield platform ++ * ++ * Copyright (C) 2013 Intel Corp ++ * Author: Samreen Nilofer ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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 _INTEL_SST_MRFLD_H ++#define _INTEL_SST_MRFLD_H ++ ++enum { ++ MERR_SALTBAY_AUDIO = 0, ++ MERR_SALTBAY_COMPR, ++ MERR_SALTBAY_VOIP, ++ MERR_SALTBAY_PROBE, ++ MERR_SALTBAY_AWARE, ++ MERR_SALTBAY_VAD, ++ MERR_SALTBAY_POWER, ++}; ++ ++enum { ++ MERR_DPCM_AUDIO = 0, ++ MERR_DPCM_DB, ++ MERR_DPCM_LL, ++ MERR_DPCM_COMPR, ++ MERR_DPCM_VOIP, ++ MERR_DPCM_PROBE, ++}; ++ ++#endif diff --git a/arch/x86/include/asm/module.h b/arch/x86/include/asm/module.h index e3b7819..a11269b 100644 --- a/arch/x86/include/asm/module.h @@ -10487,6 +10817,397 @@ index 2f366d0..2d34917 100644 +#endif + #endif /* _ASM_X86_MWAIT_H */ +diff --git a/arch/x86/include/asm/platform_byt_audio.h b/arch/x86/include/asm/platform_byt_audio.h +new file mode 100644 +index 0000000..fed2b1b +--- /dev/null ++++ b/arch/x86/include/asm/platform_byt_audio.h +@@ -0,0 +1,49 @@ ++/* ++ * platform_byt_audio.h: Baytrail audio platform data header file ++ * ++ * (C) Copyright 2013 Intel Corporation ++ * Author: Omair Md Abdullah ++ * ++ * 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. ++ */ ++#ifndef _PLATFORM_BYT_AUDIO_H_ ++#define _PLATFORM_BYT_AUDIO_H_ ++ ++enum { ++ BYT_AUD_AIF1 = 0, ++ BYT_AUD_AIF2, ++ BYT_AUD_COMPR_DEV, ++#ifdef CONFIG_SND_SOC_COMMS_SSP ++ BYT_COMMS_BT, ++ BYT_COMMS_MODEM, ++#endif /* CONFIG_SND_SOC_COMMS_SSP */ ++ BYT_AUD_PROBE_DEV, ++}; ++ ++enum { ++ BYT_CR_AUD_AIF1 = 0, ++ BYT_CR_AUD_COMPR_DEV, ++ BYT_CR_COMMS_BT, ++}; ++/* LPE viewpoint addresses */ ++/* TODO: move to DSDT */ ++#define SST_BYT_IRAM_PHY_START 0xff2c0000 ++#define SST_BYT_IRAM_PHY_END 0xff2d4000 ++#define SST_BYT_DRAM_PHY_START 0xff300000 ++#define SST_BYT_DRAM_PHY_END 0xff320000 ++#define SST_BYT_IMR_VIRT_START 0xc0000000 /* virtual addr in LPE */ ++#define SST_BYT_IMR_VIRT_END 0xc01fffff ++#define SST_BYT_SHIM_PHY_ADDR 0xff340000 ++#define SST_BYT_MBOX_PHY_ADDR 0xff344000 ++#define SST_BYT_DMA0_PHY_ADDR 0xff298000 ++#define SST_BYT_DMA1_PHY_ADDR 0xff29c000 ++#define SST_BYT_SSP0_PHY_ADDR 0xff2a0000 ++#define SST_BYT_SSP2_PHY_ADDR 0xff2a2000 ++ ++#define BYT_FW_MOD_TABLE_OFFSET 0x80000 ++#define BYT_FW_MOD_TABLE_SIZE 0x100 ++ ++#endif +diff --git a/arch/x86/include/asm/platform_mrfld_audio.h b/arch/x86/include/asm/platform_mrfld_audio.h +new file mode 100644 +index 0000000..e2b1749 +--- /dev/null ++++ b/arch/x86/include/asm/platform_mrfld_audio.h +@@ -0,0 +1,25 @@ ++/* ++ * platform_mrfld_audio.h: MRFLD audio platform data header file ++ * ++ * (C) Copyright 2012 Intel Corporation ++ * Author: Vinod Koul ++ * ++ * 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. ++ */ ++#ifndef _PLATFORM_MRFLD_AUDIO_H_ ++#define _PLATFORM_MRFLD_AUDIO_H_ ++ ++#include ++ ++struct mrfld_audio_platform_data { ++ const struct soft_platform_id *spid; ++ int codec_gpio; ++ int codec_rst; ++}; ++ ++extern void __init *merfld_audio_platform_data(void *info) __attribute__((weak)); ++extern void __init *merfld_wm8958_audio_platform_data(void *info) __attribute__((weak)); ++#endif +diff --git a/arch/x86/include/asm/platform_sst.h b/arch/x86/include/asm/platform_sst.h +new file mode 100644 +index 0000000..e752ed9 +--- /dev/null ++++ b/arch/x86/include/asm/platform_sst.h +@@ -0,0 +1,132 @@ ++ ++/* ++ * platform_sst.h: sst audio platform data header file ++ * ++ * Copyright (C) 2013 Intel Corporation ++ * Author: Dharageswari R ++ * ++ * 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. ++ */ ++ ++#ifndef _PLATFORM_SST_H_ ++#define _PLATFORM_SST_H_ ++ ++#include ++ ++#define MAX_NUM_STREAMS_CTP 5 ++#define MAX_NUM_STREAMS_MRFLD 25 ++#define MAX_NUM_STREAMS MAX_NUM_STREAMS_MRFLD ++ ++#define SST_MAX_SSP_PORTS 4 ++#define SST_MAX_DMA 2 ++ ++enum { ++ SST_SSP_AUDIO = 0, ++ SST_SSP_MODEM, ++ SST_SSP_BT, ++ SST_SSP_FM, ++}; ++ ++struct sst_gpio_config { ++ u32 i2s_rx_alt; ++ u32 i2s_tx_alt; ++ u32 i2s_frame; ++ u32 i2s_clock; ++ u32 alt_function; ++}; ++ ++struct sst_ssp_info { ++ u32 base_add; ++ struct sst_gpio_config gpio; ++ bool gpio_in_use; ++}; ++ ++struct sst_info { ++ u32 iram_start; ++ u32 iram_end; ++ bool iram_use; ++ u32 dram_start; ++ u32 dram_end; ++ bool dram_use; ++ u32 imr_start; ++ u32 imr_end; ++ bool imr_use; ++ u32 mailbox_start; ++ bool use_elf; ++ bool lpe_viewpt_rqd; ++ unsigned int max_streams; ++ u32 dma_max_len; ++ u8 num_probes; ++}; ++ ++struct sst_ssp_platform_cfg { ++ u8 ssp_cfg_sst; ++ u8 port_number; ++ u8 is_master; ++ u8 pack_mode; ++ u8 num_slots_per_frame; ++ u8 num_bits_per_slot; ++ u8 active_tx_map; ++ u8 active_rx_map; ++ u8 ssp_frame_format; ++ u8 frame_polarity; ++ u8 serial_bitrate_clk_mode; ++ u8 frame_sync_width; ++ u8 dma_handshake_interface_tx; ++ u8 dma_handshake_interface_rx; ++ u8 network_mode; ++ u8 start_delay; ++ u32 ssp_base_add; ++} __packed; ++ ++struct sst_board_config_data { ++ struct sst_ssp_platform_cfg ssp_platform_data[SST_MAX_SSP_PORTS]; ++ u8 active_ssp_ports; ++ u8 platform_id; ++ u8 board_id; ++ u8 ihf_num_chan; ++ u32 osc_clk_freq; ++} __packed; ++ ++struct sst_platform_config_data { ++ u32 sst_sram_buff_base; ++ u32 sst_dma_base[SST_MAX_DMA]; ++} __packed; ++ ++struct sst_platform_debugfs_data { ++ u32 ssp_reg_size; ++ u32 dma_reg_size; ++ u32 checkpoint_offset; ++ u32 checkpoint_size; ++ u8 num_ssp; ++ u8 num_dma; ++}; ++ ++struct sst_ipc_info { ++ int ipc_offset; ++ bool use_32bit_ops; ++ unsigned int mbox_recv_off; ++}; ++ ++struct sst_lib_dnld_info { ++ unsigned int mod_base; ++ unsigned int mod_end; ++ unsigned int mod_table_offset; ++ unsigned int mod_table_size; ++ bool mod_ddr_dnld; ++}; ++ ++struct sst_platform_info { ++ const struct sst_info *probe_data; ++ const struct sst_ssp_info *ssp_data; ++ const struct sst_board_config_data *bdata; ++ const struct sst_platform_config_data *pdata; ++ const struct sst_ipc_info *ipc_info; ++ const struct sst_platform_debugfs_data *debugfs_data; ++ const struct sst_lib_dnld_info *lib_info; ++}; ++ ++#endif +diff --git a/arch/x86/include/asm/platform_sst_audio.h b/arch/x86/include/asm/platform_sst_audio.h +new file mode 100644 +index 0000000..6333631 +--- /dev/null ++++ b/arch/x86/include/asm/platform_sst_audio.h +@@ -0,0 +1,161 @@ ++/* ++ * platform_sst_audio.h: sst audio platform data header file ++ * ++ * Copyright (C) 2012 Intel Corporation ++ * Author: Jeeja KP ++ * ++ * 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. ++ */ ++#ifndef _PLATFORM_SST_AUDIO_H_ ++#define _PLATFORM_SST_AUDIO_H_ ++ ++#include ++ ++/* The stream map status is used to dynamically assign ++ * device-id to a device, for example probe device. If ++ * a stream map entry is free for a device then the device-id ++ * for that device will be popluated when the device is ++ * opened and then the status set to IN_USE. When device ++ * is closed, the strm map status is set to FREE again. ++ */ ++enum sst_strm_map_status { ++ SST_DEV_MAP_FREE = 0, ++ SST_DEV_MAP_IN_USE, ++}; ++ ++/* Device IDs for CTP are same as stream IDs */ ++enum sst_audio_device_id_ctp { ++ SST_PCM_OUT0 = 1, ++ SST_PCM_OUT1 = 2, ++ SST_COMPRESSED_OUT = 3, ++ SST_CAPTURE_IN = 4, ++ SST_PROBE_IN = 5, ++}; ++ ++enum sst_audio_task_id_mrfld { ++ SST_TASK_ID_NONE = 0, ++ SST_TASK_ID_SBA = 1, ++ SST_TASK_ID_FBA_UL = 2, ++ SST_TASK_ID_MEDIA = 3, ++ SST_TASK_ID_AWARE = 4, ++ SST_TASK_ID_FBA_DL = 5, ++ SST_TASK_ID_MAX = SST_TASK_ID_FBA_DL, ++}; ++ ++/* Device IDs for Merrifield are Pipe IDs, ++ * ref: LPE DSP command interface spec v0.75 */ ++enum sst_audio_device_id_mrfld { ++ /* Output pipeline IDs */ ++ PIPE_ID_OUT_START = 0x0, ++ PIPE_MODEM_OUT = 0x0, ++ PIPE_BT_OUT = 0x1, ++ PIPE_CODEC_OUT0 = 0x2, ++ PIPE_CODEC_OUT1 = 0x3, ++ PIPE_SPROT_LOOP_OUT = 0x4, ++ PIPE_MEDIA_LOOP1_OUT = 0x5, ++ PIPE_MEDIA_LOOP2_OUT = 0x6, ++ PIPE_PROBE_OUT = 0x7, ++ PIPE_HF_SNS_OUT = 0x8, /* VOCIE_UPLINK_REF2 */ ++ PIPE_HF_OUT = 0x9, /* VOICE_UPLINK_REF1 */ ++ PIPE_SPEECH_OUT = 0xA, /* VOICE UPLINK */ ++ PIPE_RxSPEECH_OUT = 0xB, /* VOICE_DOWNLINK */ ++ PIPE_VOIP_OUT = 0xC, ++ PIPE_PCM0_OUT = 0xD, ++ PIPE_PCM1_OUT = 0xE, ++ PIPE_PCM2_OUT = 0xF, ++ PIPE_AWARE_OUT = 0x10, ++ PIPE_VAD_OUT = 0x11, ++ PIPE_MEDIA0_OUT = 0x12, ++ PIPE_MEDIA1_OUT = 0x13, ++ PIPE_FM_OUT = 0x14, ++ PIPE_PROBE1_OUT = 0x15, ++ PIPE_PROBE2_OUT = 0x16, ++ PIPE_PROBE3_OUT = 0x17, ++ PIPE_PROBE4_OUT = 0x18, ++ PIPE_PROBE5_OUT = 0x19, ++ PIPE_PROBE6_OUT = 0x1A, ++ PIPE_PROBE7_OUT = 0x1B, ++ PIPE_PROBE8_OUT = 0x1C, ++/* Input Pipeline IDs */ ++ PIPE_ID_IN_START = 0x80, ++ PIPE_MODEM_IN = 0x80, ++ PIPE_BT_IN = 0x81, ++ PIPE_CODEC_IN0 = 0x82, ++ PIPE_CODEC_IN1 = 0x83, ++ PIPE_SPROT_LOOP_IN = 0x84, ++ PIPE_MEDIA_LOOP1_IN = 0x85, ++ PIPE_MEDIA_LOOP2_IN = 0x86, ++ PIPE_PROBE_IN = 0x87, ++ PIPE_SIDETONE_IN = 0x88, ++ PIPE_TxSPEECH_IN = 0x89, ++ PIPE_SPEECH_IN = 0x8A, ++ PIPE_TONE_IN = 0x8B, ++ PIPE_VOIP_IN = 0x8C, ++ PIPE_PCM0_IN = 0x8D, ++ PIPE_PCM1_IN = 0x8E, ++ PIPE_MEDIA0_IN = 0x8F, ++ PIPE_MEDIA1_IN = 0x90, ++ PIPE_MEDIA2_IN = 0x91, ++ PIPE_FM_IN = 0x92, ++ PIPE_PROBE1_IN = 0x93, ++ PIPE_PROBE2_IN = 0x94, ++ PIPE_PROBE3_IN = 0x95, ++ PIPE_PROBE4_IN = 0x96, ++ PIPE_PROBE5_IN = 0x97, ++ PIPE_PROBE6_IN = 0x98, ++ PIPE_PROBE7_IN = 0x99, ++ PIPE_PROBE8_IN = 0x9A, ++ PIPE_MEDIA3_IN = 0x9C, ++ PIPE_LOW_PCM0_IN = 0x9D, ++ PIPE_RSVD = 0xFF, ++}; ++ ++/* The stream map for each platform consists of an array of the below ++ * stream map structure. The array index is used as the static stream-id ++ * associated with a device and (dev_num,subdev_num,direction) tuple match ++ * gives the device_id for the device. ++ */ ++struct sst_dev_stream_map { ++ u8 dev_num; ++ u8 subdev_num; ++ u8 direction; ++ u8 device_id; ++ u8 task_id; ++ u8 status; ++}; ++ ++#define MAX_DESCRIPTOR_SIZE 172 ++ ++struct sst_dev_effects_map { ++ char uuid[16]; ++ u16 algo_id; ++ char descriptor[MAX_DESCRIPTOR_SIZE]; ++}; ++ ++struct sst_dev_effects_resource_map { ++ char uuid[16]; ++ unsigned int flags; ++ u16 cpuLoad; ++ u16 memoryUsage; ++}; ++ ++struct sst_dev_effects { ++ struct sst_dev_effects_map *effs_map; ++ struct sst_dev_effects_resource_map *effs_res_map; ++ unsigned int effs_num_map; ++}; ++ ++struct sst_platform_data { ++ /* Intel software platform id*/ ++ const struct soft_platform_id *spid; ++ struct sst_dev_stream_map *pdev_strm_map; ++ struct sst_dev_effects pdev_effs; ++ unsigned int strm_map_size; ++}; ++ ++int add_sst_platform_device(void); ++#endif ++ diff --git a/arch/x86/include/asm/pmic_pdata.h b/arch/x86/include/asm/pmic_pdata.h new file mode 100644 index 0000000..d88c64a @@ -11197,10 +11918,10 @@ index 0000000..57886ea +obj-$(CONFIG_INTEL_DEBUG_FEATURE) += intel_soc_debug.o diff --git a/arch/x86/platform/intel-mid/board.c b/arch/x86/platform/intel-mid/board.c new file mode 100644 -index 0000000..eb9d098 +index 0000000..0f856fa --- /dev/null +++ b/arch/x86/platform/intel-mid/board.c -@@ -0,0 +1,160 @@ +@@ -0,0 +1,166 @@ +/* + * board-blackbay.c: Intel Medfield based board (Blackbay) + * @@ -11263,6 +11984,7 @@ index 0000000..eb9d098 +#include "device_libs/platform_soc_thermal.h" +#include "device_libs/platform_msic_adc.h" +#include "device_libs/platform_bcove_adc.h" ++#include +#include "device_libs/platform_mrfl_thermal.h" + +/* @@ -11278,6 +12000,8 @@ index 0000000..eb9d098 +#include "device_libs/platform_bq24261.h" +#include "device_libs/platform_pcal9555a.h" + ++#include "device_libs/platform_wm8994.h" ++ +/* + * SPI devices + */ @@ -11350,24 +12074,28 @@ index 0000000..eb9d098 + &ipc_device_handler}, + {"bcove_thrm", SFI_DEV_TYPE_IPC, 1, &mrfl_thermal_platform_data, + &ipc_device_handler}, -+ ++ {"wm8994", SFI_DEV_TYPE_I2C, 0, &wm8994_platform_data, NULL}, + /* IPC devices */ + {"pmic_charger", SFI_DEV_TYPE_IPC, 1, &no_platform_data, NULL}, + {"pmic_ccsm", SFI_DEV_TYPE_IPC, 1, &mrfl_pmic_ccsm_platform_data, + &ipc_device_handler}, + {"i2c_pmic_adap", SFI_DEV_TYPE_IPC, 1, &mrfl_pmic_i2c_platform_data, + &ipc_device_handler}, ++ {"mrfld_wm8958", SFI_DEV_TYPE_IPC, 1, &merfld_wm8958_audio_platform_data, ++ &ipc_device_handler}, + {"soc_thrm", SFI_DEV_TYPE_IPC, 1, &no_platform_data, + &soc_thrm_device_handler}, ++ {"wm8958", SFI_DEV_TYPE_I2C, 0, &wm8994_platform_data, NULL}, + {}, +}; diff --git a/arch/x86/platform/intel-mid/device_libs/Makefile b/arch/x86/platform/intel-mid/device_libs/Makefile new file mode 100644 -index 0000000..750ac8f +index 0000000..7fc1a26 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/Makefile -@@ -0,0 +1,64 @@ +@@ -0,0 +1,66 @@ +# IPC Devices ++obj-y += platform_sst_audio.o +obj-y += platform_mrfl_regulator.o +obj-y += platform_soc_thermal.o +obj-$(subst m,y,$(CONFIG_SND_BYT_MACHINE)) += platform_byt_audio.o @@ -11405,6 +12133,7 @@ index 0000000..750ac8f +obj-$(subst m,y,$(CONFIG_GPIO_PCA953X)) += platform_tca6416.o +obj-$(subst m,y,$(CONFIG_BQ24261_CHARGER)) += platform_bq24261.o +obj-$(subst m,y,$(CONFIG_GPIO_PCA953X)) += platform_pcal9555a.o ++obj-$(subst m,y,$(CONFIG_SND_SOC_WM8994)) += platform_wm8994.o +# SPI Devices +obj-$(subst m,y,$(CONFIG_SERIAL_MRST_MAX3110)) += platform_max3111.o +obj-$(subst m,y,$(CONFIG_SPI_SPIDEV)) += platform_spidev.o @@ -12073,6 +12802,241 @@ index 0000000..9f14952 + (void *dev_id, int card_present))); +#endif + +diff --git a/arch/x86/platform/intel-mid/device_libs/pci/platform_sst_pci.c b/arch/x86/platform/intel-mid/device_libs/pci/platform_sst_pci.c +new file mode 100644 +index 0000000..8721c3a +--- /dev/null ++++ b/arch/x86/platform/intel-mid/device_libs/pci/platform_sst_pci.c +@@ -0,0 +1,229 @@ ++/* ++ * platform_sst_pci.c: SST platform data initilization file ++ * ++ * (C) Copyright 2013 Intel Corporation ++ * Author: Dharageswari R ++ * ++ * 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. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define CTP_SSP_BASE 0xffa23000 ++#define CTP_DMA_BASE 0xffaf8000 ++#define MRFLD_SSP_BASE 0xff2a0000 ++#define MRFLD_DMA_BASE 0xff298000 ++#define CTP_MAX_CONFIG_SIZE 500 ++ ++#define SST_CTP_IRAM_START 0 ++#define SST_CTP_IRAM_END 0x80000 ++#define SST_CTP_DRAM_START 0x400000 ++#define SST_CTP_DRAM_END 0x480000 ++#define SSP_SIZE 0x1000 ++#define DMA_SIZE_CTP 0x1000 ++#define DMA_SIZE_MRFLD 0x4000 ++#define SST_CHECKPOINT_OFFSET 0x1C00 ++#define SST_CHECKPOINT_OFFSET_MRFLD 0x0C00 ++#define CHECKPOINT_DUMP_SZ 256 ++ ++#define SST_V1_MAILBOX_RECV 0x800 ++#define SST_V2_MAILBOX_RECV 0x400 ++ ++#define MRFLD_FW_LSP_DDR_BASE 0xC5E00000 ++#define MRFLD_FW_MOD_END (MRFLD_FW_LSP_DDR_BASE + 0x1FFFFF) ++#define MRFLD_FW_MOD_TABLE_OFFSET 0x80000 ++#define MRFLD_FW_MOD_TABLE_SIZE 0x100 ++ ++struct sst_platform_info sst_data; ++ ++static struct sst_ssp_info ssp_inf_ctp = { ++ .base_add = CTP_SSP_BASE, ++ .gpio = { ++ .alt_function = LNW_ALT_2, ++ }, ++ .gpio_in_use = true, ++}; ++ ++static struct sst_ssp_info ssp_inf_mrfld = { ++ .base_add = MRFLD_SSP_BASE, ++ .gpio_in_use = false, ++}; ++ ++static const struct sst_platform_config_data sst_ctp_pdata = { ++ .sst_sram_buff_base = 0xfffc0000, ++ .sst_dma_base[0] = CTP_DMA_BASE, ++ .sst_dma_base[1] = 0x0, ++}; ++ ++static struct sst_platform_config_data sst_mrfld_pdata = { ++ .sst_dma_base[0] = MRFLD_DMA_BASE, ++ .sst_dma_base[1] = 0x0, ++}; ++ ++static const struct sst_board_config_data sst_ctp_bdata = { ++ .active_ssp_ports = 4, ++ .platform_id = 2,/*FIXME: Once the firmware fix is available*/ ++ .board_id = 1,/*FIXME: Once the firmware fix is available*/ ++ .ihf_num_chan = 2, ++ .osc_clk_freq = 19200000, ++ .ssp_platform_data = { ++ [SST_SSP_AUDIO] = { ++ .ssp_cfg_sst = 1, ++ .port_number = 3, ++ .is_master = 1, ++ .pack_mode = 1, ++ .num_slots_per_frame = 2, ++ .num_bits_per_slot = 25, ++ .active_tx_map = 3, ++ .active_rx_map = 3, ++ .ssp_frame_format = 3, ++ .frame_polarity = 0, ++ .serial_bitrate_clk_mode = 0, ++ .frame_sync_width = 24, ++ .dma_handshake_interface_tx = 5, ++ .dma_handshake_interface_rx = 4, ++ .ssp_base_add = 0xFFA23000, ++ }, ++ [SST_SSP_MODEM] = {0}, ++ [SST_SSP_BT] = {0}, ++ [SST_SSP_FM] = {0}, ++ }, ++}; ++ ++static const struct sst_info ctp_sst_info = { ++ .iram_start = SST_CTP_IRAM_START, ++ .iram_end = SST_CTP_IRAM_END, ++ .iram_use = true, ++ .dram_start = SST_CTP_DRAM_START, ++ .dram_end = SST_CTP_DRAM_END, ++ .dram_use = true, ++ .imr_start = 0, ++ .imr_end = 0, ++ .imr_use = false, ++ .mailbox_start = 0, ++ .lpe_viewpt_rqd = false, ++ .use_elf = false, ++ .max_streams = MAX_NUM_STREAMS_CTP, ++ .dma_max_len = (SST_MAX_DMA_LEN * 4), ++ .num_probes = 1, ++}; ++ ++static const struct sst_ipc_info ctp_ipc_info = { ++ .use_32bit_ops = true, ++ .ipc_offset = 0, ++ .mbox_recv_off = SST_V1_MAILBOX_RECV, ++}; ++ ++static const struct sst_info mrfld_sst_info = { ++ .iram_start = 0, ++ .iram_end = 0, ++ .iram_use = false, ++ .dram_start = 0, ++ .dram_end = 0, ++ .dram_use = false, ++ .imr_start = 0, ++ .imr_end = 0, ++ .imr_use = false, ++ .mailbox_start = 0, ++ .use_elf = true, ++ .lpe_viewpt_rqd = false, ++ .max_streams = MAX_NUM_STREAMS_MRFLD, ++ .dma_max_len = SST_MAX_DMA_LEN_MRFLD, ++ .num_probes = 16, ++}; ++ ++static struct sst_platform_debugfs_data ctp_debugfs_data = { ++ .ssp_reg_size = SSP_SIZE, ++ .dma_reg_size = DMA_SIZE_CTP, ++ .num_ssp = 1, ++ .num_dma = 1, ++ .checkpoint_offset = SST_CHECKPOINT_OFFSET, ++ .checkpoint_size = CHECKPOINT_DUMP_SZ, ++}; ++ ++static struct sst_platform_debugfs_data mrfld_debugfs_data = { ++ .ssp_reg_size = SSP_SIZE, ++ .dma_reg_size = DMA_SIZE_MRFLD, ++ .num_ssp = 3, ++ .num_dma = 2, ++ .checkpoint_offset = SST_CHECKPOINT_OFFSET_MRFLD, ++ .checkpoint_size = CHECKPOINT_DUMP_SZ, ++}; ++ ++static const struct sst_ipc_info mrfld_ipc_info = { ++ .use_32bit_ops = false, ++ .ipc_offset = 0, ++ .mbox_recv_off = SST_V2_MAILBOX_RECV, ++}; ++ ++static const struct sst_lib_dnld_info mrfld_lib_dnld_info = { ++ .mod_base = MRFLD_FW_LSP_DDR_BASE, ++ .mod_end = MRFLD_FW_MOD_END, ++ .mod_table_offset = MRFLD_FW_MOD_TABLE_OFFSET, ++ .mod_table_size = MRFLD_FW_MOD_TABLE_SIZE, ++ .mod_ddr_dnld = true, ++}; ++ ++static int set_ctp_sst_config(struct sst_platform_info *sst_info) ++{ ++ unsigned int conf_len; ++ ++ ssp_inf_ctp.gpio.i2s_rx_alt = get_gpio_by_name("gpio_i2s3_rx"); ++ ssp_inf_ctp.gpio.i2s_tx_alt = get_gpio_by_name("gpio_i2s3_rx"); ++ ssp_inf_ctp.gpio.i2s_frame = get_gpio_by_name("gpio_i2s3_fs"); ++ ssp_inf_ctp.gpio.i2s_clock = get_gpio_by_name("gpio_i2s3_clk"); ++ ++ sst_info->ssp_data = &ssp_inf_ctp; ++ conf_len = sizeof(sst_ctp_pdata) + sizeof(sst_ctp_bdata); ++ if (conf_len > CTP_MAX_CONFIG_SIZE) ++ return -EINVAL; ++ sst_info->pdata = &sst_ctp_pdata; ++ sst_info->bdata = &sst_ctp_bdata; ++ sst_info->probe_data = &ctp_sst_info; ++ sst_info->ipc_info = &ctp_ipc_info; ++ sst_info->debugfs_data = &ctp_debugfs_data; ++ sst_info->lib_info = NULL; ++ ++ return 0; ++} ++ ++static void set_mrfld_sst_config(struct sst_platform_info *sst_info) ++{ ++ sst_info->ssp_data = &ssp_inf_mrfld; ++ sst_info->pdata = &sst_mrfld_pdata; ++ sst_info->bdata = NULL; ++ sst_info->probe_data = &mrfld_sst_info; ++ sst_info->ipc_info = &mrfld_ipc_info; ++ sst_info->debugfs_data = &mrfld_debugfs_data; ++ sst_info->lib_info = &mrfld_lib_dnld_info; ++ ++ return ; ++ ++} ++ ++static struct sst_platform_info *get_sst_platform_data(struct pci_dev *pdev) ++{ ++ struct sst_platform_info *sst_pinfo = NULL; ++ ++ set_mrfld_sst_config(&sst_data); ++ sst_pinfo = &sst_data; ++ ++ return sst_pinfo; ++} ++ ++static void sst_pci_early_quirks(struct pci_dev *pci_dev) ++{ ++ pci_dev->dev.platform_data = get_sst_platform_data(pci_dev); ++} ++ ++DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SST_MRFLD, ++ sst_pci_early_quirks); diff --git a/arch/x86/platform/intel-mid/device_libs/pci/platform_usb_otg.c b/arch/x86/platform/intel-mid/device_libs/pci/platform_usb_otg.c new file mode 100644 index 0000000..4deead8 @@ -12913,7 +13877,7 @@ index 0000000..5095329 +#endif diff --git a/arch/x86/platform/intel-mid/device_libs/platform_hsu.c b/arch/x86/platform/intel-mid/device_libs/platform_hsu.c new file mode 100644 -index 0000000..293793e +index 0000000..f1e3d29 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_hsu.c @@ -0,0 +1,440 @@ @@ -12970,7 +13934,7 @@ index 0000000..293793e + }, + [hsu_port1] = { + .id = 1, -+ .name = HSU_GPS_PORT, ++ .name = HSU_UART1_PORT, + .wake_gpio = 130, + .rx_gpio = 130, + .rx_alt = 1, @@ -12983,7 +13947,7 @@ index 0000000..293793e + }, + [hsu_port2] = { + .id = 2, -+ .name = HSU_DEBUG_PORT, ++ .name = HSU_UART2_PORT, + .wake_gpio = 134, + .rx_gpio = 134, + .rx_alt = 1, @@ -13017,7 +13981,7 @@ index 0000000..293793e + .type = gps_port, + .hw_ip = hsu_intel, + .index = 1, -+ .name = HSU_GPS_PORT, ++ .name = HSU_UART1_PORT, + .idle = 30, + .preamble = 1, + .hw_init = intel_mid_hsu_init, @@ -13033,7 +13997,7 @@ index 0000000..293793e + .type = debug_port, + .hw_ip = hsu_intel, + .index = 2, -+ .name = HSU_DEBUG_PORT, ++ .name = HSU_UART2_PORT, + .idle = 5000, + .hw_init = intel_mid_hsu_init, + .hw_set_alt = intel_mid_hsu_switch, @@ -13359,7 +14323,7 @@ index 0000000..293793e +fs_initcall(hsu_dev_platform_data); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_hsu.h b/arch/x86/platform/intel-mid/device_libs/platform_hsu.h new file mode 100644 -index 0000000..1a0182c +index 0000000..7bc6eaf --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_hsu.h @@ -0,0 +1,56 @@ @@ -13378,9 +14342,9 @@ index 0000000..1a0182c +#define _PLATFORM_HSU_H_ + +#define HSU_BT_PORT "hsu_bt_port" -+#define HSU_MODEM_PORT "hsu_modem_port" -+#define HSU_GPS_PORT "hsu_gps_port" -+#define HSU_DEBUG_PORT "hsu_debug_port" ++#define HSU_UART0_PORT "hsu_uart0_port" ++#define HSU_UART1_PORT "hsu_uart1_port" ++#define HSU_UART2_PORT "hsu_uart2_port" + +enum hsu_core { + hsu_pnw, @@ -14506,6 +15470,142 @@ index 0000000..f1011a1 +}; + +#endif +diff --git a/arch/x86/platform/intel-mid/device_libs/platform_mrfld_audio.c b/arch/x86/platform/intel-mid/device_libs/platform_mrfld_audio.c +new file mode 100644 +index 0000000..b37818e +--- /dev/null ++++ b/arch/x86/platform/intel-mid/device_libs/platform_mrfld_audio.c +@@ -0,0 +1,130 @@ ++/* ++ * platform_mrfld_audio.c: MRFLD audio platform data initilization file ++ * ++ * (C) Copyright 2012 Intel Corporation ++ * Author: Dharageswari R ++ * Vinod Koul ++ * ++ * 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. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "platform_msic.h" ++ ++static struct mrfld_audio_platform_data mrfld_audio_pdata; ++ ++void *merfld_audio_platform_data(void *info) ++{ ++ struct platform_device *pdev; ++ int ret; ++ ++ pr_debug("in %s\n", __func__); ++ ++ ret = add_sst_platform_device(); ++ if (ret < 0) { ++ pr_err("%s failed to sst_platform device\n", __func__); ++ return NULL; ++ } ++ ++ pdev = platform_device_alloc("hdmi-audio", -1); ++ if (!pdev) { ++ pr_err("failed to allocate hdmi-audio platform device\n"); ++ return NULL; ++ } ++ ++ ret = platform_device_add(pdev); ++ if (ret) { ++ pr_err("failed to add hdmi-audio platform device\n"); ++ platform_device_put(pdev); ++ return NULL; ++ } ++ ++ /* request the gpios for audio */ ++ mrfld_audio_pdata.codec_gpio = get_gpio_by_name("audiocodec_int"); ++ mrfld_audio_pdata.codec_rst = get_gpio_by_name("audiocodec_rst"); ++ ++ pdev = platform_device_alloc("mrfld_lm49453", -1); ++ if (!pdev) { ++ pr_err("failed to allocate mrfld_lm49453 platform device\n"); ++ return NULL; ++ } ++ ++ ret = platform_device_add(pdev); ++ if (ret) { ++ pr_err("failed to add mrfld_lm49453 platform device\n"); ++ platform_device_put(pdev); ++ return NULL; ++ } ++ if (platform_device_add_data(pdev, &mrfld_audio_pdata, ++ sizeof(mrfld_audio_pdata))) { ++ pr_err("failed to add mrfld_lm49453 platform data\n"); ++ platform_device_put(pdev); ++ return NULL; ++ } ++ ++ register_rpmsg_service("rpmsg_msic_mrfld_audio", RPROC_SCU, ++ RP_MSIC_MRFLD_AUDIO); ++ ++ return NULL; ++} ++ ++void *merfld_wm8958_audio_platform_data(void *info) ++{ ++ struct platform_device *pdev; ++ int ret; ++ ++ ret = add_sst_platform_device(); ++ if (ret < 0) { ++ pr_err("%s failed to sst_platform device\n", __func__); ++ return NULL; ++ } ++ ++ pdev = platform_device_alloc("hdmi-audio", -1); ++ if (!pdev) { ++ pr_err("failed to allocate hdmi-audio platform device\n"); ++ return NULL; ++ } ++ ++ ret = platform_device_add(pdev); ++ if (ret) { ++ pr_err("failed to add hdmi-audio platform device\n"); ++ platform_device_put(pdev); ++ return NULL; ++ } ++ ++ pdev = platform_device_alloc("mrfld_wm8958", -1); ++ if (!pdev) { ++ pr_err("failed to allocate mrfld_wm8958 platform device\n"); ++ return NULL; ++ } ++ ++ ret = platform_device_add(pdev); ++ if (ret) { ++ pr_err("failed to add mrfld_wm8958 platform device\n"); ++ platform_device_put(pdev); ++ return NULL; ++ } ++ if (platform_device_add_data(pdev, &mrfld_audio_pdata, ++ sizeof(mrfld_audio_pdata))) { ++ pr_err("failed to add mrfld_wm8958 platform data\n"); ++ platform_device_put(pdev); ++ return NULL; ++ } ++ ++ register_rpmsg_service("rpmsg_mrfld_wm8958_audio", RPROC_SCU, ++ RP_MSIC_MRFLD_AUDIO); ++ ++ return NULL; ++} diff --git a/arch/x86/platform/intel-mid/device_libs/platform_msic.c b/arch/x86/platform/intel-mid/device_libs/platform_msic.c new file mode 100644 index 0000000..a601f2c @@ -16232,7 +17332,7 @@ index 0000000..c8ece32 +#endif diff --git a/arch/x86/platform/intel-mid/device_libs/platform_spidev.c b/arch/x86/platform/intel-mid/device_libs/platform_spidev.c new file mode 100644 -index 0000000..8eaa92f +index 0000000..c6b3f7b --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_spidev.c @@ -0,0 +1,73 @@ @@ -16271,7 +17371,7 @@ index 0000000..8eaa92f + +static void tng_ssp_spi_cs_control(u32 command) +{ -+ gpio_set_value(tng_ssp_spi2_FS_gpio, (command == CS_ASSERT) ? 0 : 1); ++ gpio_set_value(tng_ssp_spi2_FS_gpio, (command != 0) ? 1 : 0); +} + +static void tng_ssp_spi_platform_pinmux(void) @@ -16335,6 +17435,164 @@ index 0000000..a40ef8f + +extern void *spidev_platform_data(void *info) __attribute__((weak)); +#endif +diff --git a/arch/x86/platform/intel-mid/device_libs/platform_sst_audio.c b/arch/x86/platform/intel-mid/device_libs/platform_sst_audio.c +new file mode 100644 +index 0000000..7270162 +--- /dev/null ++++ b/arch/x86/platform/intel-mid/device_libs/platform_sst_audio.c +@@ -0,0 +1,152 @@ ++/* ++ * platform_sst_libs.c: SST platform data initilization file ++ * ++ * Copyright (C) 2012 Intel Corporation ++ * Author: Jeeja KP ++ * ++ * 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. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static struct sst_platform_data sst_platform_pdata; ++ ++#if IS_BUILTIN(CONFIG_SST_MRFLD_DPCM) ++static struct sst_dev_stream_map mrfld_strm_map[] = { ++ {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, /* Reserved, not in use */ ++ {MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA1_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, ++ {MERR_DPCM_DB, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA3_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, ++ {MERR_DPCM_LL, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_LOW_PCM0_IN, SST_TASK_ID_SBA, SST_DEV_MAP_IN_USE}, ++ {MERR_DPCM_COMPR, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA0_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, ++ {MERR_DPCM_VOIP, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_VOIP_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, ++ {MERR_DPCM_PROBE, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_PROBE1_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, ++ {MERR_DPCM_PROBE, 1, SNDRV_PCM_STREAM_PLAYBACK, PIPE_PROBE2_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, ++ {MERR_DPCM_PROBE, 2, SNDRV_PCM_STREAM_PLAYBACK, PIPE_PROBE3_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, ++ {MERR_DPCM_PROBE, 3, SNDRV_PCM_STREAM_PLAYBACK, PIPE_PROBE4_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, ++ {MERR_DPCM_PROBE, 4, SNDRV_PCM_STREAM_PLAYBACK, PIPE_PROBE5_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, ++ {MERR_DPCM_PROBE, 5, SNDRV_PCM_STREAM_PLAYBACK, PIPE_PROBE6_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, ++ {MERR_DPCM_PROBE, 6, SNDRV_PCM_STREAM_PLAYBACK, PIPE_PROBE7_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, ++ {MERR_DPCM_PROBE, 7, SNDRV_PCM_STREAM_PLAYBACK, PIPE_PROBE8_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, ++ {MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_PCM1_OUT, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, ++ {MERR_DPCM_VOIP, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_VOIP_OUT, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, ++ {MERR_DPCM_PROBE, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_PROBE1_OUT, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, ++ {MERR_DPCM_PROBE, 1, SNDRV_PCM_STREAM_CAPTURE, PIPE_PROBE2_OUT, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, ++ {MERR_DPCM_PROBE, 2, SNDRV_PCM_STREAM_CAPTURE, PIPE_PROBE3_OUT, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, ++ {MERR_DPCM_PROBE, 3, SNDRV_PCM_STREAM_CAPTURE, PIPE_PROBE4_OUT, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, ++ {MERR_DPCM_PROBE, 4, SNDRV_PCM_STREAM_CAPTURE, PIPE_PROBE5_OUT, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, ++ {MERR_DPCM_PROBE, 5, SNDRV_PCM_STREAM_CAPTURE, PIPE_PROBE6_OUT, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, ++ {MERR_DPCM_PROBE, 6, SNDRV_PCM_STREAM_CAPTURE, PIPE_PROBE7_OUT, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, ++ {MERR_DPCM_PROBE, 7, SNDRV_PCM_STREAM_CAPTURE, PIPE_PROBE8_OUT, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, ++}; ++#else ++static struct sst_dev_stream_map mrfld_strm_map[] = { ++ {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, /* Reserved, not in use */ ++ {MERR_SALTBAY_AUDIO, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, ++ {MERR_SALTBAY_AUDIO, 1, SNDRV_PCM_STREAM_PLAYBACK, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, ++ {MERR_SALTBAY_AUDIO, 2, SNDRV_PCM_STREAM_PLAYBACK, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, ++ {MERR_SALTBAY_COMPR, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA0_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, ++ {MERR_SALTBAY_VOIP, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_VOIP_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, ++ {MERR_SALTBAY_AUDIO, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_PCM1_OUT, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, ++ {MERR_SALTBAY_VOIP, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_VOIP_OUT, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, ++ {MERR_SALTBAY_PROBE, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, ++ {MERR_SALTBAY_PROBE, 1, SNDRV_PCM_STREAM_CAPTURE, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, ++ {MERR_SALTBAY_PROBE, 2, SNDRV_PCM_STREAM_CAPTURE, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, ++ {MERR_SALTBAY_PROBE, 3, SNDRV_PCM_STREAM_CAPTURE, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, ++ {MERR_SALTBAY_PROBE, 4, SNDRV_PCM_STREAM_CAPTURE, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, ++ {MERR_SALTBAY_PROBE, 5, SNDRV_PCM_STREAM_CAPTURE, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, ++ {MERR_SALTBAY_PROBE, 6, SNDRV_PCM_STREAM_CAPTURE, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, ++ {MERR_SALTBAY_PROBE, 7, SNDRV_PCM_STREAM_CAPTURE, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, ++ {MERR_SALTBAY_PROBE, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, ++ {MERR_SALTBAY_PROBE, 1, SNDRV_PCM_STREAM_PLAYBACK, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, ++ {MERR_SALTBAY_PROBE, 2, SNDRV_PCM_STREAM_PLAYBACK, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, ++ {MERR_SALTBAY_PROBE, 3, SNDRV_PCM_STREAM_PLAYBACK, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, ++ {MERR_SALTBAY_PROBE, 4, SNDRV_PCM_STREAM_PLAYBACK, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, ++ {MERR_SALTBAY_PROBE, 5, SNDRV_PCM_STREAM_PLAYBACK, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, ++ {MERR_SALTBAY_PROBE, 6, SNDRV_PCM_STREAM_PLAYBACK, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, ++ {MERR_SALTBAY_PROBE, 7, SNDRV_PCM_STREAM_PLAYBACK, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, ++ {MERR_SALTBAY_AWARE, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_AWARE_OUT, SST_TASK_ID_AWARE, SST_DEV_MAP_IN_USE}, ++ {MERR_SALTBAY_VAD, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_VAD_OUT, SST_TASK_ID_AWARE, SST_DEV_MAP_IN_USE}, ++}; ++#endif ++ ++#define EQ_EFFECT_ALGO_ID 0x99 ++static struct sst_dev_effects_map mrfld_effs_map[] = { ++ { ++ {0xc1, 0x47, 0xa2, 0xf7, 0x7b, 0x1a, 0xe0, 0x11, 0x0d, 0xbb, 0x2a, 0x30, 0xdf, 0xd7, 0x20, 0x45},/* uuid */ ++ EQ_EFFECT_ALGO_ID, /* algo id */ ++ {0x00, 0x43, 0xed, 0x0b, 0xd6, 0xdd, 0xdb, 0x11, 0x34, 0x8f, 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b, /* descriptor */ ++ 0xc1, 0x47, 0xa2, 0xf7, 0x7b, 0x1a, 0xe0, 0x11, 0x0d, 0xbb, 0x2a, 0x30, 0xdf, 0xd7, 0x20, 0x45, ++ 0x12, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x45, 0x71, 0x75, 0x61, ++ 0x6c, 0x69, 0x7a, 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x74, 0x65, ++ 0x6c, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ++ }, ++ } ++}; ++ ++static struct sst_dev_effects_resource_map mrfld_effs_res_map[] = { ++ { ++ {0xc1, 0x47, 0xa2, 0xf7, 0x7b, 0x1a, 0xe0, 0x11, 0x0d, 0xbb, 0x2a, 0x30, 0xdf, 0xd7, 0x20, 0x45}, /* uuid */ ++ 0x50, /* Flags */ ++ 0x00, /* Cpu load */ ++ 0x01, /* Memory Usage */ ++ } ++}; ++ ++static void set_mrfld_platform_config(void) ++{ ++ sst_platform_pdata.pdev_strm_map = mrfld_strm_map; ++ sst_platform_pdata.strm_map_size = ARRAY_SIZE(mrfld_strm_map); ++ sst_platform_pdata.pdev_effs.effs_map = mrfld_effs_map; ++ sst_platform_pdata.pdev_effs.effs_res_map = mrfld_effs_res_map; ++ sst_platform_pdata.pdev_effs.effs_num_map = ARRAY_SIZE(mrfld_effs_map); ++} ++ ++static void populate_platform_data(void) ++{ ++ set_mrfld_platform_config(); ++} ++ ++int add_sst_platform_device(void) ++{ ++ struct platform_device *pdev = NULL; ++ int ret; ++ ++ populate_platform_data(); ++ ++ pdev = platform_device_alloc("sst-platform", -1); ++ if (!pdev) { ++ pr_err("failed to allocate audio platform device\n"); ++ return -EINVAL; ++ } ++ ++ ret = platform_device_add_data(pdev, &sst_platform_pdata, ++ sizeof(sst_platform_pdata)); ++ if (ret) { ++ pr_err("failed to add sst platform data\n"); ++ platform_device_put(pdev); ++ return -EINVAL; ++ } ++ ret = platform_device_add(pdev); ++ if (ret) { ++ pr_err("failed to add audio platform device\n"); ++ platform_device_put(pdev); ++ return -EINVAL; ++ } ++ return ret; ++} diff --git a/arch/x86/platform/intel-mid/device_libs/platform_tc35876x.c b/arch/x86/platform/intel-mid/device_libs/platform_tc35876x.c new file mode 100644 index 0000000..127091d @@ -16842,6 +18100,244 @@ index 0000000..909f697 + +extern void __init *wl12xx_platform_data(void *info) __attribute__((weak)); +#endif +diff --git a/arch/x86/platform/intel-mid/device_libs/platform_wm8994.c b/arch/x86/platform/intel-mid/device_libs/platform_wm8994.c +new file mode 100644 +index 0000000..82ada45 +--- /dev/null ++++ b/arch/x86/platform/intel-mid/device_libs/platform_wm8994.c +@@ -0,0 +1,221 @@ ++/* ++ * platform_wm8994.c: wm8994 platform data initilization file ++ * ++ * (C) Copyright 2013 Intel Corporation ++ * ++ * 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. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "platform_wm8994.h" ++ ++/***********WM89941 REGUATOR platform data*************/ ++static struct regulator_consumer_supply vwm89941_consumer[] = { ++ REGULATOR_SUPPLY("DBVDD", "1-001a"), ++ REGULATOR_SUPPLY("DBVDD1", "1-001a"), ++ REGULATOR_SUPPLY("DBVDD2", "1-001a"), ++ REGULATOR_SUPPLY("DBVDD3", "1-001a"), ++ REGULATOR_SUPPLY("AVDD2", "1-001a"), ++ REGULATOR_SUPPLY("CPVDD", "1-001a"), ++}; ++ ++static struct regulator_init_data vwm89941_data = { ++ .constraints = { ++ .always_on = 1, ++ }, ++ .num_consumer_supplies = ARRAY_SIZE(vwm89941_consumer), ++ .consumer_supplies = vwm89941_consumer, ++}; ++ ++static struct fixed_voltage_config vwm89941_config = { ++ .supply_name = "VCC_1.8V_PDA", ++ .microvolts = 1800000, ++ .gpio = -EINVAL, ++ .init_data = &vwm89941_data, ++}; ++ ++static struct platform_device vwm89941_device = { ++ .name = "reg-fixed-voltage", ++ .id = 0, ++ .dev = { ++ .platform_data = &vwm89941_config, ++ }, ++}; ++ ++/***********WM89942 REGUATOR platform data*************/ ++static struct regulator_consumer_supply vwm89942_consumer[] = { ++ REGULATOR_SUPPLY("SPKVDD1", "1-001a"), ++ REGULATOR_SUPPLY("SPKVDD2", "1-001a"), ++}; ++ ++static struct regulator_init_data vwm89942_data = { ++ .constraints = { ++ .always_on = 1, ++ }, ++ .num_consumer_supplies = ARRAY_SIZE(vwm89942_consumer), ++ .consumer_supplies = vwm89942_consumer, ++}; ++ ++static struct fixed_voltage_config vwm89942_config = { ++ .supply_name = "V_BAT", ++ .microvolts = 3700000, ++ .gpio = -EINVAL, ++ .init_data = &vwm89942_data, ++}; ++ ++static struct platform_device vwm89942_device = { ++ .name = "reg-fixed-voltage", ++ .id = 1, ++ .dev = { ++ .platform_data = &vwm89942_config, ++ }, ++}; ++ ++static struct platform_device wm8994_ldo1_device; ++static struct platform_device wm8994_ldo2_device; ++static struct platform_device *wm1811a_reg_devices[] __initdata = { ++ &vwm89941_device, ++ &vwm89942_device, ++ &wm8994_ldo1_device, ++ &wm8994_ldo2_device ++}; ++ ++static struct platform_device *wm8958_reg_devices[] __initdata = { ++ &vwm89941_device, ++ &vwm89942_device ++}; ++ ++static struct regulator_consumer_supply wm8994_avdd1_supply = ++ REGULATOR_SUPPLY("AVDD1", "1-001a"); ++ ++static struct regulator_consumer_supply wm8994_dcvdd_supply = ++ REGULATOR_SUPPLY("DCVDD", "1-001a"); ++ ++static struct regulator_init_data wm8994_ldo1_data = { ++ .constraints = { ++ .always_on = 1, ++ .name = "AVDD1_3.0V", ++ .valid_ops_mask = REGULATOR_CHANGE_STATUS, ++ }, ++ .num_consumer_supplies = 1, ++ .consumer_supplies = &wm8994_avdd1_supply, ++}; ++ ++static struct fixed_voltage_config wm8994_ldo1_config = { ++ .supply_name = "V_BAT_X", ++ .microvolts = 3700000, ++ .gpio = -EINVAL, ++ .init_data = &wm8994_ldo1_data, ++}; ++ ++static struct platform_device wm8994_ldo1_device = { ++ .name = "reg-fixed-voltage", ++ .id = 2, ++ .dev = { ++ .platform_data = &wm8994_ldo1_config, ++ }, ++}; ++ ++ ++static struct regulator_init_data wm8994_ldo2_data = { ++ .constraints = { ++ .always_on = 1, ++ .name = "DCVDD_1.0V", ++ }, ++ .num_consumer_supplies = 1, ++ .consumer_supplies = &wm8994_dcvdd_supply, ++}; ++ ++static struct fixed_voltage_config wm8994_ldo2_config = { ++ .supply_name = "V_BAT_Y", ++ .microvolts = 3700000, ++ .gpio = -EINVAL, ++ .init_data = &wm8994_ldo2_data, ++}; ++ ++static struct platform_device wm8994_ldo2_device = { ++ .name = "reg-fixed-voltage", ++ .id = 3, ++ .dev = { ++ .platform_data = &wm8994_ldo2_config, ++ }, ++}; ++ ++static struct wm8958_custom_config custom_config = { ++ .format = 6, ++ .rate = 48000, ++ .channels = 2, ++}; ++ ++static struct wm8994_pdata wm8994_pdata = { ++ /* configure gpio1 function: 0x0001(Logic level input/output) */ ++ .gpio_defaults[0] = 0x0003, ++ .irq_flags = IRQF_TRIGGER_RISING | IRQF_ONESHOT, ++ /* FIXME: Below are 1811A specfic, we need to use SPID for these */ ++ ++ /* configure gpio3/4/5/7 function for AIF2 voice */ ++ .gpio_defaults[2] = 0x8100, ++ .gpio_defaults[3] = 0x8100, ++ .gpio_defaults[4] = 0x8100, ++ .gpio_defaults[6] = 0x0100, ++ /* configure gpio8/9/10/11 function for AIF3 BT */ ++ /* gpio7 is codec intr pin for GV M2 */ ++ .gpio_defaults[7] = 0x0003, ++ .gpio_defaults[8] = 0x0105, ++ .gpio_defaults[9] = 0x0100, ++ .gpio_defaults[10] = 0x0100, ++ .ldo[0] = { 0, &wm8994_ldo1_data }, /* set actual value at wm8994_platform_data() */ ++ .ldo[1] = { 0, &wm8994_ldo2_data }, ++ .ldo_ena_always_driven = 1, ++ ++ .mic_id_delay = 300, /*300ms delay*/ ++ .micdet_delay = 500, ++ .micb_en_delay = 5000, /* Keeps MICBIAS2 high for 5sec during jack insertion/removal */ ++ ++ .custom_cfg = &custom_config, ++}; ++ ++static int wm8994_get_irq_data(struct wm8994_pdata *pdata, ++ struct i2c_board_info *i2c_info, char *name) ++{ ++ int codec_gpio; ++ ++ /* alek tells me that since driver is registering a new chip ++ * irq we need to give it a base which is unused so put ++ * 256+192 here */ ++ pdata->irq_base = (256 + 192); ++ codec_gpio = get_gpio_by_name(name); ++ if (codec_gpio < 0) { ++ pr_err("%s failed for : %d\n", __func__, codec_gpio); ++ return -EINVAL; ++ } ++ i2c_info->irq = codec_gpio + INTEL_MID_IRQ_OFFSET; ++ return codec_gpio; ++} ++ ++void __init *wm8994_platform_data(void *info) ++{ ++ struct i2c_board_info *i2c_info = (struct i2c_board_info *)info; ++ int irq = 0; ++ ++ platform_add_devices(wm8958_reg_devices, ++ ARRAY_SIZE(wm8958_reg_devices)); ++ ++ irq = wm8994_get_irq_data(&wm8994_pdata, i2c_info, ++ "audiocodec_int"); ++ if (irq < 0) ++ return NULL; ++ ++ return &wm8994_pdata; ++} +diff --git a/arch/x86/platform/intel-mid/device_libs/platform_wm8994.h b/arch/x86/platform/intel-mid/device_libs/platform_wm8994.h +new file mode 100644 +index 0000000..5abead7 +--- /dev/null ++++ b/arch/x86/platform/intel-mid/device_libs/platform_wm8994.h +@@ -0,0 +1,5 @@ ++#ifndef _PLATFORM_WM8994_H_ ++#define _PLATFORM_WM8994_H_ ++ ++extern void *wm8994_platform_data(void *info) __attribute__((weak)); ++#endif diff --git a/arch/x86/platform/intel-mid/early_printk_intel_mid.c b/arch/x86/platform/intel-mid/early_printk_intel_mid.c new file mode 100644 index 0000000..3b361bd @@ -17962,10 +19458,10 @@ index 0000000..0601fe9 +#endif diff --git a/arch/x86/platform/intel-mid/intel_mid_sfi.c b/arch/x86/platform/intel-mid/intel_mid_sfi.c new file mode 100644 -index 0000000..41f98c4 +index 0000000..f73b84e --- /dev/null +++ b/arch/x86/platform/intel-mid/intel_mid_sfi.c -@@ -0,0 +1,588 @@ +@@ -0,0 +1,608 @@ +/* + * intel_mid_sfi.c: Intel MID SFI initialization code + * @@ -18284,6 +19780,26 @@ index 0000000..41f98c4 +} +EXPORT_SYMBOL_GPL(intel_scu_devices_destroy); + ++static struct platform_device *psh_ipc; ++void intel_psh_devices_create(void) ++{ ++ psh_ipc = platform_device_alloc("intel_psh_ipc", 0); ++ if (psh_ipc == NULL) { ++ pr_err("out of memory for platform device psh_ipc.\n"); ++ return; ++ } ++ ++ platform_device_add(psh_ipc); ++} ++EXPORT_SYMBOL_GPL(intel_psh_devices_create); ++ ++void intel_psh_devices_destroy(void) ++{ ++ if (psh_ipc) ++ platform_device_del(psh_ipc); ++} ++EXPORT_SYMBOL_GPL(intel_psh_devices_destroy); ++ +void __init install_irq_resource(struct platform_device *pdev, int irq) +{ + /* Single threaded */ @@ -31189,7 +32705,7 @@ index a2b0df5..4b82961 100644 obj-$(CONFIG_INTEL_IOATDMA) += ioat/ obj-$(CONFIG_INTEL_IOP_ADMA) += iop-adma.o diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c -index a0de82e..2a51cf7 100644 +index a0de82e..91c6f8f 100644 --- a/drivers/dma/intel_mid_dma.c +++ b/drivers/dma/intel_mid_dma.c @@ -28,34 +28,41 @@ @@ -32528,7 +34044,7 @@ index a0de82e..2a51cf7 100644 + if (i < 0) { + pr_err("ERR_MDMA:Invalid ch index %x\n", i); + return; - } ++ } + /* clear the status bit */ + status = status & ~(1 << (i + mid->chan_base)); + midc = &mid->ch[i]; @@ -32546,7 +34062,7 @@ index a0de82e..2a51cf7 100644 + if (midc->block_intr_status) { + midc_collect_descriptors(mid, midc); + midc_start_descriptors(mid, midc); -+ } + } + + iowrite32(UNMASK_INTR_REG(midc->ch_id), + mid->dma_base + MASK_BLOCK); @@ -32912,7 +34428,8 @@ index a0de82e..2a51cf7 100644 + goto err_ioremap; pm_runtime_put_noidle(&pdev->dev); - pm_runtime_allow(&pdev->dev); +- pm_runtime_allow(&pdev->dev); ++ pm_runtime_forbid(&pdev->dev); return 0; -err_dma: @@ -33173,10 +34690,10 @@ index a0de82e..2a51cf7 100644 +MODULE_ALIAS("acpi:intel_dma_acpi"); diff --git a/drivers/dma/intel_mid_dma_acpi.c b/drivers/dma/intel_mid_dma_acpi.c new file mode 100644 -index 0000000..65363b4 +index 0000000..2bef1ab --- /dev/null +++ b/drivers/dma/intel_mid_dma_acpi.c -@@ -0,0 +1,177 @@ +@@ -0,0 +1,192 @@ + +/* intel_mid_dma_acpi.c - Intel MID DMA driver init file for ACPI enumaration. + * @@ -33210,17 +34727,35 @@ index 0000000..65363b4 +#include +#include + -+#define MAX_CHAN 4 /*max ch across controllers*/ +#include "intel_mid_dma_regs.h" + -+static struct device *acpi_dma_dev; ++#define HID_MAX_SIZE 8 ++ ++struct list_head dma_dev_list; + -+struct device *intel_mid_get_acpi_dma(void) ++LIST_HEAD(dma_dev_list); ++ ++struct acpi_dma_dev_list { ++ struct list_head dmadev_list; ++ char dma_hid[HID_MAX_SIZE]; ++ struct device *acpi_dma_dev; ++}; ++ ++struct device *intel_mid_get_acpi_dma(const char *hid) +{ -+ return acpi_dma_dev; ++ struct acpi_dma_dev_list *listnode; ++ if (list_empty(&dma_dev_list)) ++ return NULL; ++ ++ list_for_each_entry(listnode, &dma_dev_list, dmadev_list) { ++ if (!(strncmp(listnode->dma_hid, hid, HID_MAX_SIZE))) ++ return listnode->acpi_dma_dev; ++ } ++ return NULL; +} +EXPORT_SYMBOL_GPL(intel_mid_get_acpi_dma); + ++#if IS_ENABLED(CONFIG_ACPI) +static int mid_get_and_map_rsrc(void **dest, struct platform_device *pdev, + unsigned int num) +{ @@ -33269,7 +34804,6 @@ index 0000000..65363b4 + return 0; +} + -+#if IS_ENABLED(CONFIG_ACPI) +int dma_acpi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; @@ -33347,18 +34881,25 @@ index 0000000..65363b4 + +int dma_acpi_remove(struct platform_device *pdev) +{ -+ pm_runtime_get_noresume(&pdev->dev); -+ pm_runtime_disable(&pdev->dev); -+ acpi_dma_dev = NULL; ++ pm_runtime_forbid(&pdev->dev); + middma_shutdown(&pdev->dev); + platform_set_drvdata(pdev, NULL); + return 0; +} diff --git a/drivers/dma/intel_mid_dma_regs.h b/drivers/dma/intel_mid_dma_regs.h -index 17b4219..e82cb8d 100644 +index 17b4219..30160e4 100644 --- a/drivers/dma/intel_mid_dma_regs.h +++ b/drivers/dma/intel_mid_dma_regs.h -@@ -45,11 +45,13 @@ +@@ -31,6 +31,8 @@ + + #define INTEL_MID_DMA_DRIVER_VERSION "1.1.0" + ++#define MID_MAX_CHAN 8 /*max ch across controllers*/ ++ + #define REG_BIT0 0x00000001 + #define REG_BIT8 0x00000100 + #define INT_MASK_WE 0x8 +@@ -45,11 +47,13 @@ #define DISABLE_CHANNEL(chan_num) \ (REG_BIT8 << chan_num) @@ -33373,7 +34914,7 @@ index 17b4219..e82cb8d 100644 /*CH X REG = (DMA_CH_SIZE)*CH_NO + REG*/ #define SAR 0x00 /* Source Address Register*/ -@@ -83,6 +85,17 @@ +@@ -83,6 +87,17 @@ #define INTR_STATUS 0x360 #define DMA_CFG 0x398 #define DMA_CHAN_EN 0x3A0 @@ -33391,7 +34932,7 @@ index 17b4219..e82cb8d 100644 /*DMA channel control registers*/ union intel_mid_dma_ctl_lo { -@@ -111,6 +124,34 @@ union intel_mid_dma_ctl_lo { +@@ -111,6 +126,34 @@ union intel_mid_dma_ctl_lo { u32 llp_src_en:1; /*enable/disable source LLP = 0*/ u32 reser2:3; } ctlx; @@ -33426,7 +34967,7 @@ index 17b4219..e82cb8d 100644 u32 ctl_lo; }; -@@ -120,8 +161,13 @@ union intel_mid_dma_ctl_hi { +@@ -120,8 +163,13 @@ union intel_mid_dma_ctl_hi { u32 done:1; /*Done - updated by DMAC*/ u32 reser:19; /*configured by DMAC*/ } ctlx; @@ -33441,7 +34982,7 @@ index 17b4219..e82cb8d 100644 }; /*DMA channel configuration registers*/ -@@ -141,6 +187,33 @@ union intel_mid_dma_cfg_lo { +@@ -141,6 +189,33 @@ union intel_mid_dma_cfg_lo { u32 reload_src:1; /*auto reload src addr =1 if src is P*/ u32 reload_dst:1; /*AR destn addr =1 if dstn is P*/ } cfgx; @@ -33475,7 +35016,7 @@ index 17b4219..e82cb8d 100644 u32 cfg_lo; }; -@@ -154,9 +227,43 @@ union intel_mid_dma_cfg_hi { +@@ -154,9 +229,43 @@ union intel_mid_dma_cfg_hi { u32 dst_per:4; /*dstn hw HS interface*/ u32 reser2:17; } cfgx; @@ -33519,7 +35060,7 @@ index 17b4219..e82cb8d 100644 /** * struct intel_mid_dma_chan - internal mid representation of a DMA channel -@@ -168,13 +275,14 @@ union intel_mid_dma_cfg_hi { +@@ -168,13 +277,14 @@ union intel_mid_dma_cfg_hi { * @active_list: current active descriptors * @queue: current queued up descriptors * @free_list: current free descriptors @@ -33537,7 +35078,7 @@ index 17b4219..e82cb8d 100644 */ struct intel_mid_dma_chan { struct dma_chan chan; -@@ -192,6 +300,8 @@ struct intel_mid_dma_chan { +@@ -192,6 +302,8 @@ struct intel_mid_dma_chan { u32 raw_tfr; u32 raw_block; struct intel_mid_dma_slave *mid_slave; @@ -33546,7 +35087,7 @@ index 17b4219..e82cb8d 100644 }; static inline struct intel_mid_dma_chan *to_intel_mid_dma_chan( -@@ -207,6 +317,8 @@ enum intel_mid_dma_state { +@@ -207,6 +319,8 @@ enum intel_mid_dma_state { /** * struct middma_device - internal representation of a DMA device * @pdev: PCI device @@ -33555,7 +35096,7 @@ index 17b4219..e82cb8d 100644 * @dma_base: MMIO register space pointer of DMA * @dma_pool: for allocating DMA descriptors * @common: embedded struct dma_device -@@ -220,9 +332,12 @@ enum intel_mid_dma_state { +@@ -220,14 +334,17 @@ enum intel_mid_dma_state { * @block_size: Block size of DMA transfer supported (from drv_data) * @pimr_mask: MMIO register addr for periphral interrupt (from drv_data) * @state: dma PM device state @@ -33569,7 +35110,13 @@ index 17b4219..e82cb8d 100644 void __iomem *dma_base; struct pci_pool *dma_pool; struct dma_device common; -@@ -235,7 +350,13 @@ struct middma_device { + struct tasklet_struct tasklet; +- struct intel_mid_dma_chan ch[MAX_CHAN]; ++ struct intel_mid_dma_chan ch[MID_MAX_CHAN]; + unsigned int pci_id; + unsigned int intr_mask; + void __iomem *mask_reg; +@@ -235,7 +352,13 @@ struct middma_device { int max_chan; int block_size; unsigned int pimr_mask; @@ -33583,7 +35130,7 @@ index 17b4219..e82cb8d 100644 }; static inline struct middma_device *to_middma_device(struct dma_device *common) -@@ -266,15 +387,30 @@ struct intel_mid_dma_desc { +@@ -266,15 +389,30 @@ struct intel_mid_dma_desc { enum intel_mid_dma_mode cfg_mode; /*mode configuration*/ }; @@ -33618,7 +35165,7 @@ index 17b4219..e82cb8d 100644 static inline int test_ch_en(void __iomem *dma, u32 ch_no) { u32 en_reg = ioread32(dma + DMA_CHAN_EN); -@@ -294,6 +430,12 @@ static inline struct intel_mid_dma_slave *to_intel_mid_dma_slave +@@ -294,6 +432,12 @@ static inline struct intel_mid_dma_slave *to_intel_mid_dma_slave } @@ -35946,7 +37493,7 @@ index 325013a..d011e91 100644 #include "intel_bios.h" #include "psb_drv.h" diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig -index df064e8..3899d25 100644 +index df064e8..877aa4d 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -504,6 +504,16 @@ config SENSORS_CORETEMP @@ -35966,7 +37513,7 @@ index df064e8..3899d25 100644 config SENSORS_IBMAEM tristate "IBM Active Energy Manager temperature/power sensors and control" select IPMI_SI -@@ -822,6 +832,12 @@ config SENSORS_LM95245 +@@ -822,6 +832,21 @@ config SENSORS_LM95245 This driver can also be built as a module. If so, the module will be called lm95245. @@ -35975,19 +37522,29 @@ index df064e8..3899d25 100644 + depends on INTEL_SCU_IPC + help + Say Y here to enable MSIC GPADC driver on Intel Medfield Platform ++ ++config INTEL_MCU ++ tristate "Intel generic MCU control interface" ++ help ++ Say Y here to enable control interface for intel mcu ++ ++ This driver provide userspace tty interface for the control and ++ message output. ++ You could use normal read/write to complete those operation. + config SENSORS_MAX1111 tristate "Maxim MAX1111 Serial 8-bit ADC chip and compatibles" depends on SPI_MASTER diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile -index d17d3e6..4e1d647 100644 +index d17d3e6..11e757a 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile -@@ -140,6 +140,7 @@ obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o +@@ -140,6 +140,8 @@ obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o +obj-$(CONFIG_MSIC_GPADC) += intel_mid_gpadc.o ++obj-$(CONFIG_INTEL_MCU) += intel_mcu_common.o obj-$(CONFIG_PMBUS) += pmbus/ @@ -36518,6 +38075,797 @@ index 658ce3a..7d091ab 100644 kfree(pdata->core_data[indx]); pdata->core_data[indx] = NULL; +diff --git a/drivers/hwmon/intel_mcu_common.c b/drivers/hwmon/intel_mcu_common.c +new file mode 100644 +index 0000000..cdf078c +--- /dev/null ++++ b/drivers/hwmon/intel_mcu_common.c +@@ -0,0 +1,700 @@ ++/** ++ * intel_mcu_common.c - Intel MCU common interface file ++ * ++ * Copyright (C) 2014 Intel Inc. - http://www.intel.com ++ * ++ * Authors: Lei Wen , ++ * ++ * 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 the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not 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. ++ * ++ * 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 MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "intel_mcu_common.h" ++#include ++ ++#define APP_IMR_SIZE (1024 * 126) ++#define DRIVER_AUTHOR "Lei Wen " ++#define DRIVER_DESC "Intel mcu common control interface" ++#define INTEL_MCU_TTY_MAJOR 168 ++#define INTEL_MCU_TTY_MINORS 3 ++ ++#define LOAD_APP "load mcu app" ++#define GET_VERSION "get mcu app version" ++struct tty_driver *intel_mcu_tty_driver; ++ ++#define VER_LEN 1024 ++struct mcu { ++ char ver[VER_LEN]; ++ uintptr_t ddr_phy[2]; ++ void *ddr[2]; ++ int load_in_progress; ++}; ++ ++struct mcu_data { ++ struct device *dev; ++ struct tty_port port; ++ struct mcu *mcu; ++ struct completion cmp; ++ struct loop_buffer lbuf; ++ int index; ++}; ++ ++static struct mcu_data *mcu_table[INTEL_MCU_TTY_MINORS]; ++static int log_level = 1; ++static char *debug_msg[] = { ++ "fatal", ++ "error", ++ "warning", ++ "info", ++ "debug", ++}; ++ ++static int send_cmd(struct mcu_data *data, ++ struct psh_msg *in, int ch, int wait) ++{ ++ int ret; ++ ret = intel_ia2psh_command(in, NULL, ch, 1000000); ++ if (ret) ++ return ret; ++ ++ if (wait) { ++ ret = wait_for_completion_timeout(&data->cmp, 3 * HZ); ++ if (ret == 0) ++ return -ETIME; ++ } ++ ++ return 0; ++} ++ ++static void lbuf_read_reset(struct loop_buffer *lbuf) ++{ ++ if (lbuf) { ++ lbuf->off_head = lbuf->off_tail = 0; ++ lbuf->in_reading = 0; ++ } ++} ++ ++static int lbuf_read_next(struct loop_buffer *lbuf, u8 **buf, u16 *size) ++{ ++ struct frame_head *fhead = ++ (struct frame_head *)(lbuf->addr + lbuf->off_head); ++ *buf = NULL; ++ *size = 0; ++ ++ if (lbuf->in_reading) { ++ lbuf->in_reading = 0; ++ ++ /* go over previous frame has been read */ ++ lbuf->off_head += frame_size(fhead->length); ++ lbuf->off_tail = lbuf->off_head; ++ fhead = (struct frame_head *)(lbuf->addr + lbuf->off_head); ++ } ++ ++ if (fhead->sign == LBUF_DISCARD_SIGN) { ++ fhead = (struct frame_head *)lbuf->addr; ++ lbuf->off_head = lbuf->off_tail = 0; ++ } ++ ++ if (fhead->sign == LBUF_CELL_SIGN) { ++ ++ *buf = lbuf->addr + lbuf->off_head + sizeof(*fhead); ++ *size = fhead->length; ++ lbuf->in_reading = 1; ++ } ++ ++ return !lbuf->in_reading; ++} ++ ++static int intel_mcu_mcudbg_level(struct mcu_data *data, int level) ++{ ++ struct psh_msg in; ++ struct cmd_debug_param *param; ++ ++ in.param = 0; ++ in.msg = CMD_MCU_APP_DEBUG; ++ param = (struct cmd_debug_param *) (&(in.param)); ++ if (level > 0) { ++ param->level = level; ++ param->sub_cmd = CMD_DEBUG_SET_MASK; ++ } else ++ param->sub_cmd = CMD_DEBUG_GET_MASK; ++ ++ return send_cmd(data, &in, PSH2IA_CHANNEL2, 1); ++} ++ ++static void push_char_into_port(struct tty_port *port, const char *buf, int len) ++{ ++ int count; ++ ++ if (len <= 0) ++ return; ++ ++ do { ++ count = tty_insert_flip_string(port, buf, len); ++ len -= count; ++ buf += count; ++ } while (len > 0); ++ ++ tty_flip_buffer_push(port); ++} ++ ++static int intel_mcu_tty_open(struct tty_struct *tty, struct file *filp) ++{ ++ dev_dbg(tty->dev, "%s\n", __func__); ++ tty->driver_data = mcu_table[tty->index]; ++ /* ++ * For we may get data cached while we don't open this tty, ++ * so we need to flush out buffer, then we could ++ * get full content without disappoint user ++ */ ++ if (tty->port) ++ tty_flip_buffer_push(tty->port); ++ ++ return 0; ++} ++ ++static void intel_mcu_tty_close(struct tty_struct *tty, struct file *filp) ++{ ++ dev_dbg(tty->dev, "%s\n", __func__); ++ tty->driver_data = NULL; ++} ++ ++static int do_get_ver(struct mcu_data *data) ++{ ++ struct psh_msg in; ++ ++ in.param = 0; ++ in.msg = CMD_MCU_APP_GET_VERSION; ++ return send_cmd(data, &in, PSH2IA_CHANNEL2, 1); ++} ++ ++static int do_setup_ddr(struct mcu_data *data) ++{ ++ struct mcu *mcu = data->mcu; ++ const struct firmware *fw_entry; ++ static int fw_load_done; ++ char fname[20]; ++ struct psh_msg in; ++ ++ if (fw_load_done) ++ return 0; ++ ++ snprintf(fname, 20, "intel_mcu.bin"); ++ if (!request_firmware(&fw_entry, fname, data->dev)) { ++ if (!fw_entry) ++ return -ENOMEM; ++ ++ pr_debug("psh fw size %d virt:0x%p\n", ++ (int)fw_entry->size, fw_entry->data); ++ if (fw_entry->size > APP_IMR_SIZE) { ++ pr_err("psh fw size too big\n"); ++ } else { ++ memcpy(mcu->ddr[0], fw_entry->data, ++ fw_entry->size); ++ in.msg = CMD_MCU_LOAD_APP; ++ in.param = mcu->ddr_phy[0]; ++ mcu->load_in_progress = 1; ++ if (send_cmd(data, &in, PSH2IA_CHANNEL3, 1)) ++ return -1; ++ fw_load_done = 1; ++ } ++ release_firmware(fw_entry); ++ } else { ++ pr_err("cannot find psh firmware(%s)\n", fname); ++ return -ENODEV; ++ } ++ in.msg = CMD_MCU_SETUP_DDR; ++ in.param = mcu->ddr_phy[1]; ++ return send_cmd(data, &in, PSH2IA_CHANNEL2, 1); ++} ++ ++static ssize_t load_app_store(struct device *device, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ int len = strlen(LOAD_APP); ++ ++ if (count >= len && strncmp(buf, LOAD_APP, len) == 0) { ++ do_setup_ddr(mcu_table[2]); ++ return count; ++ } ++ ++ pr_err("Please provide right string as [%s]!\n", LOAD_APP); ++ return -1; ++} ++ ++static ssize_t get_ver_show(struct device *device, ++ struct device_attribute *attr, char *buf) ++{ ++ struct mcu_data *data = mcu_table[2]; ++ struct mcu *mcu = data->mcu; ++ ++ if (do_get_ver(data)) ++ return -1; ++ ++ return scnprintf(buf, VER_LEN, "%s", mcu->ver); ++} ++ ++static ssize_t mdbg_control_show(struct device *device, ++ struct device_attribute *attr, char *buf) ++{ ++ if (intel_mcu_mcudbg_level(mcu_table[2], -1) < 0) ++ goto err; ++ ++ if (log_level > 0 && log_level < 6) ++ return scnprintf(buf, 8, "%s\n", debug_msg[log_level - 1]); ++ ++err: ++ pr_info("get log level err\n"); ++ return -1; ++} ++/* ++ *set msg level:echo log_level=fatal|info|warning|error|debug| >control ++*/ ++#define LOG_LEVEL "fatal|error|warning|info|debug" ++static ssize_t mdbg_control_store(struct device *device, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct mcu_data *data = mcu_table[2]; ++ int level = 0; ++ long ltmp = 0; ++ ++ if (!buf) ++ return -1; ++ if (!strncmp(buf, "fatal", strlen("fatal"))) ++ level = 1; ++ else if (!strncmp(buf, "error", strlen("error"))) ++ level = 2; ++ else if (!strncmp(buf, "warning", strlen("warning"))) ++ level = 3; ++ else if (!strncmp(buf, "info", strlen("info"))) ++ level = 4; ++ else if (!strncmp(buf, "debug", strlen("debug"))) ++ level = 5; ++ else { ++ int err; ++ err = kstrtol(buf, 10, <mp); ++ if (!err && (ltmp > 0) && (ltmp < 6)) ++ level = ltmp; ++ else { ++ pr_err("Please input words as [%s]\n", LOG_LEVEL); ++ return -1; ++ } ++ } ++ pr_info("set level:%d\n", level); ++ if (intel_mcu_mcudbg_level(data, level) < 0) ++ return -1; ++ return count; ++} ++ ++static DEVICE_ATTR(control, 0200, NULL, load_app_store); ++static DEVICE_ATTR(fw_version, 0400, get_ver_show, NULL); ++static DEVICE_ATTR(log_level, 0600, mdbg_control_show, mdbg_control_store); ++ ++static struct attribute *control_sysfs_attrs[] = { ++ &dev_attr_control.attr, ++ &dev_attr_fw_version.attr, ++ &dev_attr_log_level.attr, ++ NULL, ++ ++}; ++ ++static struct attribute_group intel_mcu_tty_attribute_group = { ++ .name = NULL, ++ .attrs = control_sysfs_attrs, ++ ++}; ++ ++static void raw_output(struct mcu_data *data, int ch, ++ const unsigned char *buf, int count) ++{ ++ struct psh_msg in; ++ int i, left; ++ ++ for (i = 0; i < count; i += 4) { ++ left = count - i; ++ if (left > 4) { ++ left = 4; ++ in.msg = PSH_IPC_CONTINUE; ++ } else ++ in.msg = 0; ++ ++ memcpy(&in.param, buf, left); ++ buf += left; ++ send_cmd(data, &in, ch, 0); ++ } ++} ++ ++#define TTY_WRITE_ROOM 512 ++static int intel_mcu_tty_write(struct tty_struct *tty, ++ const unsigned char *buf, int count) ++{ ++ struct mcu_data *data = tty->driver_data; ++ ++ switch (tty->index) { ++ default: ++ pr_err("TTY index %d not supported!\n", tty->index); ++ case 1: ++ return -1; ++ case 0: ++ if (count > TTY_WRITE_ROOM) { ++ pr_err("Port 0's input size is limited by %d!\n", ++ TTY_WRITE_ROOM); ++ return -1; ++ } ++ raw_output(data, tty->index, buf, count); ++ break; ++ } ++ return count; ++} ++ ++static int intel_mcu_tty_write_room(struct tty_struct *tty) ++{ ++ return TTY_WRITE_ROOM; ++} ++ ++static const struct tty_operations intel_mcu_ops = { ++ .open = intel_mcu_tty_open, ++ .close = intel_mcu_tty_close, ++ .write = intel_mcu_tty_write, ++ .write_room = intel_mcu_tty_write_room, ++}; ++ ++static int mem_alloc(struct pci_dev *pdev, uintptr_t *phy_addr, ++ void **virt_addr, int bar) ++{ ++ void __iomem *mem; ++ int ret = 0; ++ unsigned long start = 0, len; ++ ++ /* dedicate isolated memory region */ ++ start = pci_resource_start(pdev, bar); ++ len = pci_resource_len(pdev, bar); ++ if (!start || !len) { ++ dev_err(&pdev->dev, "bar %d address not set\n", bar); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ ret = pci_request_region(pdev, bar, "intel_mcu"); ++ if (ret) { ++ dev_err(&pdev->dev, ++ "failed to request psh region 0x%lx-0x%lx\n", ++ start, ++ (unsigned long)pci_resource_end(pdev, bar)); ++ goto err; ++ } ++ ++ mem = ioremap_nocache(start, len); ++ if (!mem) { ++ dev_err(&pdev->dev, "can not ioremap app imr address\n"); ++ ret = -EINVAL; ++ goto err_ioremap; ++ } ++ ++ *phy_addr = start; ++ *virt_addr = (void *)mem; ++ return 0; ++ ++err_ioremap: ++ pci_release_region(pdev, bar); ++err: ++ return ret; ++} ++ ++static void cmd_handler(u32 msg, u32 param, void *_data) ++{ ++ struct mcu_data *data = (struct mcu_data *)_data; ++ struct mcu *mcu = data->mcu; ++ struct cmd_resp *resp; ++ const struct version_resp *version; ++ struct debug_resp *debug_resp; ++ u8 *dbuf = NULL; ++ u16 size = 0; ++ ++ if (mcu->load_in_progress) { ++ mcu->load_in_progress = 0; ++ goto done; ++ } ++ ++ while (!lbuf_read_next(&data->lbuf, &dbuf, &size)) { ++ resp = (struct cmd_resp *)dbuf; ++ ++ if (!resp->len) ++ continue; ++ ++ switch (resp->cmd_id) { ++ case CMD_MCU_APP_GET_VERSION: ++ version = (struct version_resp *)resp->param; ++ if (version->total_length) ++ snprintf(mcu->ver, VER_LEN, version->buf, ++ version->total_length); ++ break; ++ case CMD_MCU_APP_DEBUG: ++ debug_resp = (struct debug_resp *)resp->param; ++ log_level = debug_resp->level; ++ default: ++ break; ++ } ++ } ++done: ++ complete(&data->cmp); ++} ++ ++static void raw_data_handler(u32 msg, u32 param, void *_data) ++{ ++ struct mcu_data *data = (struct mcu_data *)_data; ++ struct cmd_resp *resp; ++ u8 *dbuf = NULL; ++ u16 size = 0; ++ ++ while (!lbuf_read_next(&data->lbuf, &dbuf, &size)) { ++ resp = (struct cmd_resp *)dbuf; ++ push_char_into_port(&data->port, resp->param, resp->len); ++ } ++ complete(&data->cmp); ++} ++ ++static int mcu_platform_probe(struct platform_device *pdev) ++{ ++ int ret, i; ++ struct mcu_data *data; ++ struct mcu *mcu; ++ u8 *base; ++ ++ mcu = platform_get_drvdata(pdev); ++ intel_mcu_tty_driver = alloc_tty_driver(INTEL_MCU_TTY_MINORS); ++ if (!intel_mcu_tty_driver) { ++ dev_err(&pdev->dev, "fail to alloc tty driver\n"); ++ return -ENODEV; ++ } ++ ++ intel_mcu_tty_driver->name = "ttymcu"; ++ intel_mcu_tty_driver->major = INTEL_MCU_TTY_MAJOR; ++ intel_mcu_tty_driver->minor_start = 0; ++ intel_mcu_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; ++ intel_mcu_tty_driver->subtype = SERIAL_TYPE_NORMAL; ++ intel_mcu_tty_driver->flags = TTY_DRIVER_REAL_RAW ++ | TTY_DRIVER_DYNAMIC_DEV; ++ intel_mcu_tty_driver->init_termios = tty_std_termios; ++ intel_mcu_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | ++ HUPCL | CLOCAL; ++ intel_mcu_tty_driver->init_termios.c_ispeed = 38400; ++ intel_mcu_tty_driver->init_termios.c_ospeed = 38400; ++ intel_mcu_tty_driver->init_termios.c_iflag = 0; ++ intel_mcu_tty_driver->init_termios.c_oflag = 0; ++ intel_mcu_tty_driver->init_termios.c_lflag = 0; ++ tty_set_operations(intel_mcu_tty_driver, &intel_mcu_ops); ++ ++ ret = tty_register_driver(intel_mcu_tty_driver); ++ if (ret) { ++ dev_err(&pdev->dev, "fail to register tty driver\n"); ++ goto tty_reg_fail; ++ } ++ ++ base = (u8 *)mcu->ddr[1]; ++ for (i = INTEL_MCU_TTY_MINORS - 1; i >= 0; i--) { ++ data = kzalloc(sizeof(struct mcu_data), GFP_KERNEL); ++ if (data == NULL) { ++ dev_err(&pdev->dev, "fail to alloc mcu data\n"); ++ goto data_alloc_fail; ++ } ++ ++ data->index = i; ++ tty_port_init(&data->port); ++ data->dev = tty_port_register_device(&data->port, ++ intel_mcu_tty_driver, i, &pdev->dev); ++ mcu_table[i] = data; ++ data->mcu = mcu; ++ init_completion(&data->cmp); ++ data->lbuf.addr = base; ++ data->lbuf.length = BUF_IA_DDR_SIZE; ++ lbuf_read_reset(&data->lbuf); ++ base += BUF_IA_DDR_SIZE; ++ } ++ ret = sysfs_create_group(&pdev->dev.kobj, ++ &intel_mcu_tty_attribute_group); ++ if (ret) { ++ pr_err("failed to create the mdbg sysfs attributes\n"); ++ sysfs_remove_group(&pdev->dev.kobj, ++ &intel_mcu_tty_attribute_group); ++ goto data_alloc_fail; ++ } ++ ++ intel_psh_ipc_bind(PSH_RECV_CH0, raw_data_handler, mcu_table[0]); ++ intel_psh_ipc_bind(PSH_RECV_CH1, raw_data_handler, mcu_table[1]); ++ intel_psh_ipc_bind(PSH_RECV_CH2, cmd_handler, mcu_table[2]); ++ ++ pr_info("MCU detected and ready to used!\n"); ++ ++ return 0; ++ ++data_alloc_fail: ++ for (i = 0; i < INTEL_MCU_TTY_MINORS; i++) ++ kfree(mcu_table[i]); ++tty_reg_fail: ++ put_tty_driver(intel_mcu_tty_driver); ++ return ret; ++} ++ ++static int mcu_platform_remove(struct platform_device *pdev) ++{ ++ struct mcu *mcu; ++ int i; ++ ++ mcu = platform_get_drvdata(pdev); ++ sysfs_remove_group(&pdev->dev.kobj, ++ &intel_mcu_tty_attribute_group); ++ ++ for (i = 0; i < INTEL_MCU_TTY_MINORS; i++) ++ kfree(mcu_table[i]); ++ put_tty_driver(intel_mcu_tty_driver); ++ kfree(mcu); ++ ++ return 0; ++} ++ ++static struct platform_driver intel_mcu_platform = { ++ .driver = { ++ .name = "intel_mcu", ++ }, ++ .probe = mcu_platform_probe, ++ .remove = mcu_platform_remove, ++}; ++module_platform_driver(intel_mcu_platform); ++ ++static int intel_mcu_probe(struct pci_dev *pdev, const struct pci_device_id *id) ++{ ++ struct platform_device *dev; ++ struct mcu *mcu; ++ int ret; ++ ++ ret = pci_enable_device(pdev); ++ if (ret) { ++ dev_err(&pdev->dev, "fail to enable psh pci device\n"); ++ return -ENODEV; ++ } ++ ++ mcu = kzalloc(sizeof(struct mcu), GFP_KERNEL); ++ if (!mcu) { ++ dev_err(&pdev->dev, "cannot allocate memory for mcu\n"); ++ ret = -ENOMEM; ++ goto mcu_err; ++ } ++ ++ ret = mem_alloc(pdev, &mcu->ddr_phy[0], &mcu->ddr[0], 0); ++ if (ret) ++ goto plat_alloc_fail; ++ ++ ret = mem_alloc(pdev, &mcu->ddr_phy[1], &mcu->ddr[1], 1); ++ if (ret) ++ goto plat_alloc_fail; ++ ++ dev = platform_device_alloc("intel_mcu", -1); ++ if (!dev) { ++ ret = -ENODEV; ++ goto plat_alloc_fail; ++ } ++ ++ dev->dev.dma_mask = &dev->dev.coherent_dma_mask; ++ platform_set_drvdata(dev, mcu); ++ dev_set_drvdata(&pdev->dev, mcu); ++ ++ ret = platform_device_add(dev); ++ return ret; ++ ++plat_alloc_fail: ++ kfree(mcu); ++mcu_err: ++ pci_dev_put(pdev); ++ return ret; ++} ++ ++static void intel_mcu_remove(struct pci_dev *pdev) ++{ ++ struct mcu *mcu; ++ ++ mcu = dev_get_drvdata(&pdev->dev); ++ iounmap((void __iomem *)mcu->ddr[0]); ++ iounmap((void __iomem *)mcu->ddr[1]); ++ ++ pci_release_region(pdev, 0); ++ pci_release_region(pdev, 1); ++ pci_dev_put(pdev); ++} ++ ++static DEFINE_PCI_DEVICE_TABLE(pci_ids) = { ++ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x11a4)}, ++ { 0,} ++}; ++ ++MODULE_DEVICE_TABLE(pci, pci_ids); ++static struct pci_driver intel_mcu_driver = { ++ .name = "intel_mcu", ++ .id_table = pci_ids, ++ .probe = intel_mcu_probe, ++ .remove = intel_mcu_remove, ++}; ++ ++static int __init intel_mcu_init(void) ++{ ++ return pci_register_driver(&intel_mcu_driver); ++} ++ ++static void __exit intel_mcu_exit(void) ++{ ++ pci_unregister_driver(&intel_mcu_driver); ++} ++ ++module_init(intel_mcu_init); ++module_exit(intel_mcu_exit); ++MODULE_AUTHOR(DRIVER_AUTHOR); ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS_CHARDEV_MAJOR(INTEL_MCU_TTY_MAJOR); +diff --git a/drivers/hwmon/intel_mcu_common.h b/drivers/hwmon/intel_mcu_common.h +new file mode 100644 +index 0000000..1ffd081 +--- /dev/null ++++ b/drivers/hwmon/intel_mcu_common.h +@@ -0,0 +1,79 @@ ++#ifndef _EDISON_COMMON_H_ ++#define _EDISON_COMMON_H_ ++ ++#define PSH2IA_CHANNEL0 0 ++#define PSH2IA_CHANNEL1 1 ++#define PSH2IA_CHANNEL2 2 ++#define PSH2IA_CHANNEL3 3 ++ ++enum cmd_id { ++ CMD_MCU_LOAD_APP = 0, ++ CMD_MCU_SETUP_DDR, ++ CMD_MCU_APP_DEBUG, ++ CMD_MCU_APP_GET_VERSION, ++}; ++ ++#define CIRC_SIZE (1024 * 64) ++struct ddr_param { ++ u32 ddr; ++ u32 ddr1; ++} __packed; ++ ++#define CMD_DEBUG_SET_MASK ((u8)0x1) ++#define CMD_DEBUG_GET_MASK ((u8)0x2) ++#define MCU_DBG_ALL ((u16)-1) ++#define MCU_DBG_FATAL 1 ++#define MCU_DBG_ERR 2 ++#define MCU_DBG_WARN 3 ++#define MCU_DBG_INFO 4 ++#define MCU_DBG_DBG 5 ++ ++struct cmd_debug_param { ++ u8 sub_cmd; ++ u16 level; ++ char tag[30]; ++} __packed; ++ ++#define RESP_PARAM_MAX_SIZE 56 ++struct cmd_resp { ++ u8 cmd_id; ++ u8 len; ++ int ret; ++ char param[RESP_PARAM_MAX_SIZE]; ++} __packed; ++ ++struct debug_resp { ++ u16 level; ++} __packed; ++ ++struct version_resp { ++ u8 total_length; ++ u8 segment_length; ++ u8 sequence_number; ++ char buf[0]; ++} __packed; ++ ++#define LBUF_CELL_SIGN ((u16)0x4853) ++#define LBUF_EMPTY_SIGN ((u16)0x0000) ++#define LBUF_DISCARD_SIGN ((u16)0x4944) ++#define size_align(size) ((size % 4) ? (size + 4 - (size % 4)) : size) ++#define frame_size(size) (size_align(size) + \ ++ sizeof(struct frame_head)) ++ ++struct frame_head { ++ u16 sign; ++ u16 length; ++ u8 buf[0]; ++} __packed; ++ ++#define BUF_IA_DDR_SIZE 8192 ++struct loop_buffer { ++ int in_reading; ++ u8 *addr; ++ u16 length; ++ ++ u16 off_head; ++ u16 off_tail; ++}; ++ ++#endif diff --git a/drivers/hwmon/intel_mid_gpadc.c b/drivers/hwmon/intel_mid_gpadc.c new file mode 100644 index 0000000..f4d0d2f @@ -45118,24 +47466,268 @@ index 0000000..1fb2d2e + +#endif /* _STM_H */ diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c -index dd27b07..72bab34 100644 +index dd27b07..1137130 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c -@@ -161,6 +161,12 @@ static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) - return md; +@@ -96,6 +96,7 @@ struct mmc_blk_data { + #define MMC_BLK_CMD23 (1 << 0) /* Can do SET_BLOCK_COUNT for multiblock */ + #define MMC_BLK_REL_WR (1 << 1) /* MMC Reliable write support */ + #define MMC_BLK_PACKED_CMD (1 << 2) /* MMC packed command support */ ++#define MMC_BLK_SUSPENDED (1 << 3) /* MMC block device suspended */ + + unsigned int usage; + unsigned int read_only; +@@ -106,7 +107,8 @@ struct mmc_blk_data { + #define MMC_BLK_WRITE BIT(1) + #define MMC_BLK_DISCARD BIT(2) + #define MMC_BLK_SECDISCARD BIT(3) +- ++#define MMC_BLK_RPMB BIT(4) ++#define MMC_BLK_USER BIT(5) + /* + * Only set in main mmc_blk_data associated + * with mmc_card with mmc_set_drvdata, and keeps +@@ -146,6 +148,9 @@ static inline void mmc_blk_clear_packed(struct mmc_queue_req *mqrq) + packed->blocks = 0; + } + ++static int mmc_rpmb_req_process(struct mmc_blk_data *, ++ struct mmc_ioc_rpmb_req *); ++ + static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) + { + struct mmc_blk_data *md; +@@ -568,12 +573,53 @@ cmd_err: + return err; + } + ++static int mmc_blk_ioctl_rpmb_req(struct block_device *bdev, ++ struct mmc_ioc_rpmb_req __user *ptr) ++{ ++ struct mmc_ioc_rpmb_req req; ++ struct mmc_blk_data *md = NULL; ++ int err = 0; ++ ++ /* The caller must have CAP_SYS_RAWIO */ ++ if (!capable(CAP_SYS_RAWIO)) ++ return -EPERM; ++ ++ memset(&req, 0, sizeof(req)); ++ ++ if (copy_from_user(&req, ptr, sizeof(req))) ++ return -EFAULT; ++ ++ md = mmc_blk_get(bdev->bd_disk); ++ if (!md) { ++ pr_err("%s: NO eMMC block data. Try it later\n", ++ __func__); ++ return -ENODEV; ++ } ++ /* handle RPMB request event */ ++ err = mmc_rpmb_req_process(md, &req); ++ if (err) { ++ mmc_blk_put(md); ++ return err; ++ } ++ /* ++ * feedback to user space ++ */ ++ if (copy_to_user(ptr, &req, sizeof(req))) ++ return -EFAULT; ++ ++ mmc_blk_put(md); ++ return 0; ++} ++ + static int mmc_blk_ioctl(struct block_device *bdev, fmode_t mode, + unsigned int cmd, unsigned long arg) + { + int ret = -EINVAL; + if (cmd == MMC_IOC_CMD) + ret = mmc_blk_ioctl_cmd(bdev, (struct mmc_ioc_cmd __user *)arg); ++ else if (cmd == MMC_IOC_RPMB_REQ) ++ ret = mmc_blk_ioctl_rpmb_req(bdev, ++ (struct mmc_ioc_rpmb_req __user *)arg); + return ret; + } + +@@ -891,6 +937,114 @@ static inline void mmc_blk_reset_success(struct mmc_blk_data *md, int type) + md->reset_done &= ~type; } ++static int mmc_rpmb_req_process(struct mmc_blk_data *md, ++ struct mmc_ioc_rpmb_req *req) ++{ ++ struct mmc_core_rpmb_req rpmb_req; ++ struct mmc_card *card = NULL; ++ int ret; ++ ++ if (!md || !req) ++ return -EINVAL; ++ ++ if (!(md->flags & MMC_BLK_CMD23) || ++ (md->part_type != EXT_CSD_PART_CONFIG_ACC_RPMB)) ++ return -EOPNOTSUPP; ++ ++ card = md->queue.card; ++ if (!card || !mmc_card_mmc(card) || !card->ext_csd.rpmb_size) ++ return -ENODEV; ++ ++ memset(&rpmb_req, 0, sizeof(struct mmc_core_rpmb_req)); ++ rpmb_req.req = req; ++ /* check request */ ++ ret = mmc_rpmb_pre_frame(&rpmb_req, card); ++ if (ret) { ++ pr_err("%s: prepare frame failed\n", mmc_hostname(card->host)); ++ return ret; ++ } ++ ++ mmc_claim_host(card->host); ++ ++ if (md->flags & MMC_BLK_SUSPENDED) { ++ pr_warn("%s: MMC block device is already suspended\n", ++ mmc_hostname(card->host)); ++ ret = -EPERM; ++ goto out; ++ } ++ /* ++ * before start, let's change to RPMB partition first ++ */ ++ ret = mmc_blk_part_switch(card, md); ++ if (ret) { ++ pr_err("%s: Invalid RPMB partition switch (%d)!\n", ++ mmc_hostname(card->host), ret); ++ /* ++ * In case partition is not in user data area, make ++ * a force partition switch. ++ * we need reset eMMC card at here ++ */ ++ ret = mmc_blk_reset(md, card->host, MMC_BLK_RPMB); ++ if (!ret) ++ mmc_blk_reset_success(md, MMC_BLK_RPMB); ++ else ++ pr_err("%s: eMMC card reset failed (%d)\n", ++ mmc_hostname(card->host), ret); ++ goto out; ++ } ++ ++ ret = mmc_rpmb_partition_ops(&rpmb_req, card); ++ if (ret) ++ pr_err("%s: failed (%d) to handle RPMB request type (%d)!\n", ++ mmc_hostname(card->host), ret, req->type); ++out: ++ mmc_release_host(card->host); ++ mmc_rpmb_post_frame(&rpmb_req); ++ return ret; ++} ++ +int mmc_access_rpmb(struct mmc_queue *mq) +{ + struct mmc_blk_data *md = mq->data; -+ return md && md->part_type == EXT_CSD_PART_CONFIG_ACC_RPMB; ++ /* ++ * If this is a RPMB partition access, return ture ++ */ ++ if (md && md->part_type == EXT_CSD_PART_CONFIG_ACC_RPMB) ++ return true; ++ ++ return false; ++} ++EXPORT_SYMBOL_GPL(mmc_access_rpmb); ++ ++int mmc_rpmb_req_handle(struct device *emmc, struct mmc_ioc_rpmb_req *req) ++{ ++ int ret = 0; ++ struct gendisk *disk = NULL; ++ struct mmc_blk_data *md = NULL; ++ ++ if (!emmc || !req) ++ return -EINVAL; ++ ++ disk = dev_to_disk(emmc); ++ if (!disk) { ++ pr_err("%s: NO eMMC disk found. Try it later\n", ++ __func__); ++ return -ENODEV; ++ } ++ ++ md = mmc_blk_get(disk); ++ if (!md) { ++ pr_err("%s: NO eMMC block data. Try it later\n", ++ __func__); ++ return -ENODEV; ++ } ++ ret = mmc_rpmb_req_process(md, req); ++ mmc_blk_put(md); ++ ++ return ret; +} ++EXPORT_SYMBOL_GPL(mmc_rpmb_req_handle); + - static inline int mmc_get_devidx(struct gendisk *disk) + static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req) { - int devmaj = MAJOR(disk_devt(disk)); + struct mmc_blk_data *md = mq->data; +@@ -1899,11 +2053,16 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) + + ret = mmc_blk_part_switch(card, md); + if (ret) { +- if (req) { +- blk_end_request_all(req, -EIO); ++ pr_err("%s: switch part failed. Try to reset eMMC\n", ++ mmc_hostname(card->host)); ++ if (mmc_blk_reset(md, card->host, MMC_BLK_USER)) { ++ if (req) ++ blk_end_request_all(req, -EIO); ++ ret = 0; ++ goto out; + } +- ret = 0; +- goto out; ++ pr_info("%s: Reset eMMC success\n", mmc_hostname(card->host)); ++ mmc_blk_reset_success(md, MMC_BLK_USER); + } + + mq->flags &= ~MMC_QUEUE_NEW_REQUEST; +@@ -2366,6 +2525,19 @@ static int mmc_blk_suspend(struct mmc_card *card) + mmc_queue_suspend(&md->queue); + list_for_each_entry(part_md, &md->part, part) { + mmc_queue_suspend(&part_md->queue); ++ if (part_md->part_type == ++ EXT_CSD_PART_CONFIG_ACC_RPMB) { ++ /* ++ * RPMB partition is accessed by API directly. ++ * Driver need to set a flag when suspending ++ * MMC block device to notify API that the ++ * accessing of RPMB partition needs to be ++ * stopped ++ */ ++ mmc_claim_host(card->host); ++ part_md->flags |= MMC_BLK_SUSPENDED; ++ mmc_release_host(card->host); ++ } + } + } + return 0; +@@ -2385,6 +2557,18 @@ static int mmc_blk_resume(struct mmc_card *card) + mmc_queue_resume(&md->queue); + list_for_each_entry(part_md, &md->part, part) { + mmc_queue_resume(&part_md->queue); ++ if (part_md->part_type == ++ EXT_CSD_PART_CONFIG_ACC_RPMB) { ++ /* ++ * RPMB partition is accessed by API directly. ++ * Driver need to clear MMC_BLK_SUSPENDED flag ++ * to make sure the next RPMB partition access ++ * request won't be blocked ++ */ ++ mmc_claim_host(card->host); ++ part_md->flags &= ~MMC_BLK_SUSPENDED; ++ mmc_release_host(card->host); ++ } + } + } + return 0; diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c -index 9447a0e..2c84ec5 100644 +index 9447a0e..88327f7 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -18,6 +18,7 @@ @@ -45151,19 +47743,19 @@ index 9447a0e..2c84ec5 100644 } - if (mq && mmc_card_removed(mq->card)) -+ if (mq && mmc_card_removed(mq->card) || mmc_access_rpmb(mq)) ++ if (mq && (mmc_card_removed(mq->card) || mmc_access_rpmb(mq))) return BLKPREP_KILL; req->cmd_flags |= REQ_DONTPREP; diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h -index 5752d50..23460e6 100644 +index 5752d50..3bbd4e6 100644 --- a/drivers/mmc/card/queue.h +++ b/drivers/mmc/card/queue.h @@ -73,4 +73,5 @@ extern void mmc_queue_bounce_post(struct mmc_queue_req *); extern int mmc_packed_init(struct mmc_queue *, struct mmc_card *); extern void mmc_packed_clean(struct mmc_queue *); -+int mmc_access_rpmb(struct mmc_queue *mq); ++extern int mmc_access_rpmb(struct mmc_queue *); #endif diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index 38ed210..d55cc5d 100644 @@ -45302,7 +47894,7 @@ index 35c2f85..a0cb347 100644 str = "invalid"; break; diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c -index 0cbd1ef..89d6082 100644 +index 0cbd1ef..fc39179 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -239,7 +239,8 @@ static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd) @@ -45328,7 +47920,51 @@ index 0cbd1ef..89d6082 100644 card->ext_csd.hs_max_dtr = hs_max_dtr; card->ext_csd.card_type = card_type; } -@@ -707,8 +714,12 @@ static int mmc_select_powerclass(struct mmc_card *card, +@@ -497,6 +504,9 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) + * RPMB regions are defined in multiples of 128K. + */ + card->ext_csd.raw_rpmb_size_mult = ext_csd[EXT_CSD_RPMB_MULT]; ++ card->ext_csd.rpmb_size = 128 * ++ card->ext_csd.raw_rpmb_size_mult; ++ card->ext_csd.rpmb_size <<= 2; /* Unit: half sector */ + if (ext_csd[EXT_CSD_RPMB_MULT] && mmc_host_cmd23(card->host)) { + mmc_part_add(card, ext_csd[EXT_CSD_RPMB_MULT] << 17, + EXT_CSD_PART_CONFIG_ACC_RPMB, +@@ -548,6 +558,17 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) + card->ext_csd.data_sector_size = 512; + } + ++ /* ++ * If use legacy reliable write, then the blk counts must not ++ * big than the reliable write sectors ++ */ ++ if (!(card->ext_csd.rel_param & EXT_CSD_WR_REL_PARAM_EN)) { ++ if (card->ext_csd.rel_sectors < RPMB_AVALIABLE_SECTORS) ++ card->rpmb_max_req = card->ext_csd.rel_sectors; ++ else ++ card->rpmb_max_req = RPMB_AVALIABLE_SECTORS; ++ } else ++ card->rpmb_max_req = RPMB_AVALIABLE_SECTORS; + out: + return err; + } +@@ -635,6 +656,7 @@ MMC_DEV_ATTR(enhanced_area_offset, "%llu\n", + MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size); + MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult); + MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors); ++MMC_DEV_ATTR(rpmb_size, "%d\n", card->ext_csd.rpmb_size); + + static struct attribute *mmc_std_attrs[] = { + &dev_attr_cid.attr, +@@ -653,6 +675,7 @@ static struct attribute *mmc_std_attrs[] = { + &dev_attr_enhanced_area_size.attr, + &dev_attr_raw_rpmb_size_mult.attr, + &dev_attr_rel_sectors.attr, ++ &dev_attr_rpmb_size.attr, + NULL, + }; + +@@ -707,8 +730,12 @@ static int mmc_select_powerclass(struct mmc_card *card, index = (bus_width <= EXT_CSD_BUS_WIDTH_8) ? EXT_CSD_PWR_CL_52_195 : EXT_CSD_PWR_CL_DDR_52_195; @@ -45343,7 +47979,7 @@ index 0cbd1ef..89d6082 100644 break; case MMC_VDD_27_28: case MMC_VDD_28_29: -@@ -755,6 +766,81 @@ static int mmc_select_powerclass(struct mmc_card *card, +@@ -755,6 +782,81 @@ static int mmc_select_powerclass(struct mmc_card *card, } /* @@ -45425,7 +48061,7 @@ index 0cbd1ef..89d6082 100644 * Selects the desired buswidth and switch to the HS200 mode * if bus width set without error */ -@@ -775,12 +861,16 @@ static int mmc_select_hs200(struct mmc_card *card) +@@ -775,12 +877,16 @@ static int mmc_select_hs200(struct mmc_card *card) host = card->host; @@ -45446,7 +48082,7 @@ index 0cbd1ef..89d6082 100644 err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); /* If fails try again during next card power cycle */ -@@ -1038,8 +1128,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, +@@ -1038,8 +1144,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, */ if (card->ext_csd.hs_max_dtr != 0) { err = 0; @@ -45458,7 +48094,7 @@ index 0cbd1ef..89d6082 100644 err = mmc_select_hs200(card); else if (host->caps & MMC_CAP_MMC_HIGHSPEED) err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, -@@ -1055,6 +1147,15 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, +@@ -1055,6 +1163,15 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, err = 0; } else { if (card->ext_csd.hs_max_dtr > 52000000 && @@ -45474,7 +48110,7 @@ index 0cbd1ef..89d6082 100644 host->caps2 & MMC_CAP2_HS200) { mmc_card_set_hs200(card); mmc_set_timing(card->host, -@@ -1071,7 +1172,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, +@@ -1071,7 +1188,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, */ max_dtr = (unsigned int)-1; @@ -45485,7 +48121,7 @@ index 0cbd1ef..89d6082 100644 if (max_dtr > card->ext_csd.hs_max_dtr) max_dtr = card->ext_csd.hs_max_dtr; if (mmc_card_highspeed(card) && (max_dtr > 52000000)) -@@ -1099,9 +1202,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, +@@ -1099,9 +1218,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } /* @@ -45497,7 +48133,7 @@ index 0cbd1ef..89d6082 100644 u32 ext_csd_bits; u32 bus_width = card->host->ios.bus_width; -@@ -1116,7 +1219,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, +@@ -1116,7 +1235,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, * 3. set the clock to > 52Mhz <=200MHz and * 4. execute tuning for HS200 */ @@ -45508,7 +48144,7 @@ index 0cbd1ef..89d6082 100644 card->host->ops->execute_tuning) { mmc_host_clk_hold(card->host); err = card->host->ops->execute_tuning(card->host, -@@ -1129,19 +1234,45 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, +@@ -1129,19 +1250,45 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, goto err; } @@ -45558,7 +48194,7 @@ index 0cbd1ef..89d6082 100644 (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) { static unsigned ext_csd_bits[][2] = { diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c -index 49f04bc..a3d0615 100644 +index 49f04bc..432af79 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -141,7 +141,7 @@ int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) @@ -45579,6 +48215,422 @@ index 49f04bc..a3d0615 100644 } if (rocr && !mmc_host_is_spi(host)) +@@ -638,3 +638,415 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status) + + return 0; + } ++ ++static int mmc_rpmb_send_command(struct mmc_card *card, u8 *buf, __u16 blks, ++ __u16 type, u8 req_type) ++{ ++ struct mmc_request mrq = {NULL}; ++ struct mmc_command cmd = {0}; ++ struct mmc_command sbc = {0}; ++ struct mmc_data data = {0}; ++ struct scatterlist sg; ++ u8 *transfer_buf = NULL; ++ ++ mrq.sbc = &sbc; ++ mrq.cmd = &cmd; ++ mrq.data = &data; ++ mrq.stop = NULL; ++ transfer_buf = kzalloc(512 * blks, GFP_KERNEL); ++ if (!transfer_buf) ++ return -ENOMEM; ++ ++ /* ++ * set CMD23 ++ */ ++ sbc.opcode = MMC_SET_BLOCK_COUNT; ++ sbc.arg = blks; ++ if ((req_type == RPMB_REQ) && (type == RPMB_WRITE_DATA || ++ type == RPMB_PROGRAM_KEY)) ++ sbc.arg |= 1 << 31; ++ sbc.flags = MMC_RSP_R1 | MMC_CMD_AC; ++ ++ /* ++ * set CMD25/18 ++ */ ++ sg_init_one(&sg, transfer_buf, 512 * blks); ++ if (req_type == RPMB_REQ) { ++ cmd.opcode = MMC_WRITE_MULTIPLE_BLOCK; ++ sg_copy_from_buffer(&sg, 1, buf, 512 * blks); ++ data.flags |= MMC_DATA_WRITE; ++ } else { ++ cmd.opcode = MMC_READ_MULTIPLE_BLOCK; ++ data.flags |= MMC_DATA_READ; ++ } ++ cmd.arg = 0; ++ cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; ++ data.blksz = 512; ++ data.blocks = blks; ++ data.sg = &sg; ++ data.sg_len = 1; ++ ++ mmc_set_data_timeout(&data, card); ++ ++ mmc_wait_for_req(card->host, &mrq); ++ ++ if (req_type != RPMB_REQ) ++ sg_copy_to_buffer(&sg, 1, buf, 512 * blks); ++ ++ kfree(transfer_buf); ++ ++ if (cmd.error) ++ return cmd.error; ++ if (data.error) ++ return data.error; ++ return 0; ++} ++ ++void mmc_rpmb_post_frame(struct mmc_core_rpmb_req *rpmb_req) ++{ ++ int i; ++ struct mmc_ioc_rpmb_req *p_req; ++ __u8 *buf_frame; ++ ++ if (!rpmb_req || !rpmb_req->ready) ++ return; ++ ++ p_req = rpmb_req->req; ++ buf_frame = rpmb_req->frame; ++ ++ if (!p_req || !buf_frame) ++ return; ++ /* ++ * Regarding to the check rules, here is the post ++ * rules ++ * All will return result. ++ * GET_WRITE_COUNTER: ++ * must: write counter, nonce ++ * optional: MAC ++ * WRITE_DATA: ++ * must: MAC, write counter ++ * READ_DATA: ++ * must: nonce, data ++ * optional: MAC ++ * PROGRAM_KEY: ++ * must: Nothing ++ * ++ * Except READ_DATA, all of these operations only need to parse ++ * one frame. READ_DATA needs blks frames to get DATA ++ */ ++ ++ memcpy(p_req->result, buf_frame + RPMB_RES_BEG, 2); ++ *p_req->result = be16_to_cpup(p_req->result); ++ ++ if (p_req->type == RPMB_PROGRAM_KEY) ++ goto out; ++ ++ if (p_req->type == RPMB_GET_WRITE_COUNTER || ++ p_req->type == RPMB_WRITE_DATA) { ++ memcpy(p_req->wc, buf_frame + RPMB_WCOUNTER_BEG, 4); ++ *p_req->wc = be32_to_cpup(p_req->wc); ++ } ++ ++ if (p_req->type == RPMB_GET_WRITE_COUNTER || ++ p_req->type == RPMB_READ_DATA) { ++ /* nonce copy */ ++ memcpy(p_req->nonce, buf_frame + RPMB_NONCE_BEG, 16); ++ } ++ /* ++ * Take MAC within the last package ++ */ ++ if (p_req->type == RPMB_READ_DATA) { ++ __u8 *data = p_req->data; ++ for (i = 0; i < p_req->blk_cnt; i++) { ++ memcpy(data, buf_frame + i * 512 + RPMB_DATA_BEG, 256); ++ data += 256; ++ } ++ /* ++ * MAC stored in the last package ++ */ ++ if (p_req->mac) ++ memcpy(p_req->mac, buf_frame + i * 512 + RPMB_MAC_BEG, ++ 32); ++ } else if (p_req->mac) ++ memcpy(p_req->mac, buf_frame + RPMB_MAC_BEG, 32); ++out: ++ kfree(buf_frame); ++ rpmb_req->frame = NULL; ++ return; ++} ++EXPORT_SYMBOL_GPL(mmc_rpmb_post_frame); ++ ++static int mmc_rpmb_request_check(struct mmc_card *card, ++ struct mmc_ioc_rpmb_req *p_req) ++{ ++ /* ++ * Some parameters are a must for the operation. Different ++ * operation expect different paramters. Below code is ++ * used for checking this. ++ * ++ * All operations will need result. ++ * GET_WRITE_COUNTER: ++ * must: write counter, nonce ++ * optional: MAC ++ * WRITE_DATA: ++ * must: MAC, data, write counter ++ * READ_DATA: ++ * must: nonce, data ++ * optional: MAC ++ * PROGRAM_KEY: ++ * must: MAC ++ * ++ * So here, we only check the 'must' paramters ++ */ ++ if (!p_req->result) { ++ pr_err("%s: Type %d has NULL pointer for result\n", ++ mmc_hostname(card->host), p_req->type); ++ return -EINVAL; ++ } ++ ++ if (p_req->type == RPMB_GET_WRITE_COUNTER) { ++ if (!p_req->nonce || !p_req->wc) { ++ pr_err("%s: Type %d has NULL pointer for nonce/wc\n", ++ mmc_hostname(card->host), p_req->type); ++ return -EINVAL; ++ } ++ /* ++ * used to allocate frame ++ */ ++ p_req->blk_cnt = 1; ++ } else if (p_req->type == RPMB_WRITE_DATA || ++ p_req->type == RPMB_READ_DATA) { ++ if ((__u32)(p_req->addr + p_req->blk_cnt) > ++ card->ext_csd.rpmb_size) { ++ pr_err("%s Type %d: beyond the RPMB partition rang addr %d, blk_cnt %d, rpmb_size %d\n", ++ mmc_hostname(card->host), ++ p_req->type, ++ p_req->addr, ++ p_req->blk_cnt, ++ card->ext_csd.rpmb_size); ++ return -EINVAL; ++ } ++ if (p_req->blk_cnt == 0) { ++ pr_err("%s: Type %d has zero block count\n", ++ mmc_hostname(card->host), ++ p_req->blk_cnt); ++ return -EINVAL; ++ } else if (p_req->blk_cnt > card->rpmb_max_req) { ++ pr_err("%s: Type %d has invalid block count, cannot large than %d\n", ++ mmc_hostname(card->host), ++ p_req->blk_cnt, ++ card->rpmb_max_req); ++ return -EINVAL; ++ } ++ if (!p_req->data) { ++ pr_err("%s: Type %d has NULL pointer for data\n", ++ mmc_hostname(card->host), p_req->type); ++ return -EINVAL; ++ } ++ if (p_req->type == RPMB_WRITE_DATA) { ++ if (!p_req->wc || !p_req->mac) { ++ pr_err("%s: Type %d has NULL pointer for write counter/MAC\n", ++ mmc_hostname(card->host), ++ p_req->type); ++ return -EINVAL; ++ } ++ } else { ++ if (!p_req->nonce) { ++ pr_err("%s: Type %d has NULL pointer for nonce\n", ++ mmc_hostname(card->host), ++ p_req->type); ++ return -EINVAL; ++ } ++ } ++ } else if (p_req->type == RPMB_PROGRAM_KEY) { ++ if (!p_req->mac) { ++ pr_err("%s: Type %d has NULL pointer for MAC\n", ++ mmc_hostname(card->host), p_req->type); ++ return -EINVAL; ++ } ++ /* ++ * used to allocate frame ++ */ ++ p_req->blk_cnt = 1; ++ } else ++ return -EOPNOTSUPP; ++ ++ return 0; ++} ++ ++/* ++ * prepare the request of RPMB frame ++ * RPMB frame is MSB first ++ * convert needed bytes ++ * return how many frames will be prepared ++ */ ++int mmc_rpmb_pre_frame(struct mmc_core_rpmb_req *rpmb_req, ++ struct mmc_card *card) ++{ ++ int i, ret; ++ struct mmc_ioc_rpmb_req *p_req; ++ __u8 *buf_frame; ++ __u16 blk_cnt, addr, type; ++ __u32 w_counter; ++ ++ if (!rpmb_req || !card) ++ return -EINVAL; ++ ++ p_req = rpmb_req->req; ++ if (!p_req) { ++ pr_err("%s: mmc_ioc_rpmb_req is NULL. Wrong parameter\n", ++ mmc_hostname(card->host)); ++ return -EINVAL; ++ } ++ ++ /* ++ * make sure these two items are clear ++ */ ++ rpmb_req->ready = 0; ++ rpmb_req->frame = NULL; ++ ++ ret = mmc_rpmb_request_check(card, p_req); ++ if (ret) ++ return ret; ++ ++ buf_frame = kzalloc(512 * p_req->blk_cnt, GFP_KERNEL); ++ if (!buf_frame) { ++ pr_err("%s: cannot allocate frame for type %d\n", ++ mmc_hostname(card->host), p_req->type); ++ return -ENOMEM; ++ } ++ ++ type = cpu_to_be16p(&p_req->type); ++ if (p_req->type == RPMB_GET_WRITE_COUNTER || ++ p_req->type == RPMB_READ_DATA) { ++ /* ++ * One package prepared ++ * This request needs Nonce and type ++ * If is data read, then also need addr ++ */ ++ memcpy(buf_frame + RPMB_TYPE_BEG, &type, 2); ++ if (p_req->type == RPMB_READ_DATA) { ++ addr = cpu_to_be16p(&p_req->addr); ++ memcpy(buf_frame + RPMB_ADDR_BEG, &addr, 2); ++ } ++ /* convert Nonce code */ ++ memcpy(buf_frame + RPMB_NONCE_BEG, p_req->nonce, 16); ++ } else if (p_req->type == RPMB_WRITE_DATA) { ++ __u8 *data = p_req->data; ++ /* ++ * multiple package prepared ++ * This request nees blk_cnt, addr, write_counter, ++ * data and mac ++ */ ++ blk_cnt = cpu_to_be16p(&p_req->blk_cnt); ++ addr = cpu_to_be16p(&p_req->addr); ++ w_counter = cpu_to_be32p(p_req->wc); ++ for (i = 0; i < p_req->blk_cnt; i++) { ++ memcpy(buf_frame + i * 512 + RPMB_TYPE_BEG, ++ &type, 2); ++ memcpy(buf_frame + i * 512 + RPMB_BLKS_BEG, ++ &blk_cnt, 2); ++ memcpy(buf_frame + i * 512 + RPMB_ADDR_BEG, ++ &addr, 2); ++ memcpy(buf_frame + i * 512 + RPMB_WCOUNTER_BEG, ++ &w_counter, 4); ++ memcpy(buf_frame + i * 512 + RPMB_DATA_BEG, ++ data, 256); ++ data += 256; ++ } ++ /* convert MAC code */ ++ memcpy(buf_frame + 512 * (i - 1) + RPMB_MAC_BEG, ++ p_req->mac, 32); ++ } else if (p_req->type == RPMB_PROGRAM_KEY) { ++ /* ++ * One package prepared ++ * This request only need mac ++ */ ++ memcpy(buf_frame + RPMB_TYPE_BEG, &type, 2); ++ /* convert MAC code */ ++ memcpy(buf_frame + RPMB_MAC_BEG, ++ p_req->mac, 32); ++ } else { ++ pr_err("%s: We shouldn't be here\n", mmc_hostname(card->host)); ++ kfree(buf_frame); ++ return -EINVAL; ++ } ++ rpmb_req->ready = 1; ++ rpmb_req->frame = buf_frame; ++ return 0; ++} ++EXPORT_SYMBOL_GPL(mmc_rpmb_pre_frame); ++ ++int mmc_rpmb_partition_ops(struct mmc_core_rpmb_req *rpmb_req, ++ struct mmc_card *card) ++{ ++ int err = 0; ++ struct mmc_ioc_rpmb_req *p_req; ++ __u16 type, blks; ++ __u8 *buf_frame; ++ ++ if (!rpmb_req || !card) ++ return -EINVAL; ++ ++ p_req = rpmb_req->req; ++ buf_frame = rpmb_req->frame; ++ ++ if (!p_req || !rpmb_req->ready || !buf_frame) { ++ pr_err("%s: mmc_ioc_rpmb_req is not prepared\n", ++ mmc_hostname(card->host)); ++ return -EINVAL; ++ } ++ ++ type = p_req->type; ++ blks = p_req->blk_cnt; ++ ++ /* ++ * STEP 1: send request to RPMB partition ++ */ ++ if (type == RPMB_WRITE_DATA) ++ err = mmc_rpmb_send_command(card, buf_frame, blks, ++ type, RPMB_REQ); ++ else ++ err = mmc_rpmb_send_command(card, buf_frame, 1, type, RPMB_REQ); ++ ++ if (err) { ++ pr_err("%s: request write counter failed (%d)\n", ++ mmc_hostname(card->host), err); ++ goto out; ++ } ++ ++ memset(buf_frame, 0, 512 * blks); ++ /* ++ * STEP 2: check write result ++ * Only for WRITE_DATA or Program key ++ */ ++ if (type == RPMB_WRITE_DATA || ++ type == RPMB_PROGRAM_KEY) { ++ buf_frame[RPMB_TYPE_BEG + 1] = RPMB_RESULT_READ; ++ err = mmc_rpmb_send_command(card, buf_frame, 1, ++ RPMB_RESULT_READ, RPMB_REQ); ++ if (err) { ++ pr_err("%s: request write counter failed (%d)\n", ++ mmc_hostname(card->host), err); ++ goto out; ++ } ++ } ++ ++ /* ++ * STEP 3: get response from RPMB partition ++ */ ++ ++ if (type == RPMB_READ_DATA) ++ err = mmc_rpmb_send_command(card, buf_frame, ++ blks, type, RPMB_RESP); ++ else ++ err = mmc_rpmb_send_command(card, buf_frame, ++ 1, type, RPMB_RESP); ++ if (err) { ++ pr_err("%s: response write counter failed (%d)\n", ++ mmc_hostname(card->host), err); ++ } ++out: ++ return err; ++} ++EXPORT_SYMBOL_GPL(mmc_rpmb_partition_ops); diff --git a/drivers/mmc/core/mmc_panic_ops.c b/drivers/mmc/core/mmc_panic_ops.c new file mode 100644 index 0000000..2ed0204 @@ -47699,7 +50751,7 @@ index 701d06d..39696af 100644 .pm = &sdhci_pci_pm_ops }, diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c -index 2ea429c..8289777 100644 +index 2ea429c..1f7c796 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -54,6 +54,7 @@ static void sdhci_finish_command(struct sdhci_host *); @@ -48431,7 +51483,12 @@ index 2ea429c..8289777 100644 /* The controller does not support the end-of-busy IRQ, * fall through and take the SDHCI_INT_RESPONSE */ -@@ -2306,7 +2701,10 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) +@@ -2303,10 +2698,23 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) + * The "data complete" interrupt is also used to + * indicate that a busy state has ended. See comment + * above in sdhci_cmd_irq(). ++ * ++ * "data timeout" interrupt may also happen */ if (host->cmd && (host->cmd->flags & MMC_RSP_BUSY)) { if (intmask & SDHCI_INT_DATA_END) { @@ -48440,10 +51497,18 @@ index 2ea429c..8289777 100644 + sdhci_finish_command(host); + else + host->r1b_busy_end = 1; ++ return; ++ } else if (intmask & SDHCI_INT_DATA_TIMEOUT) { ++ pr_err("%s: Got data interrupt 0x%08x for busy cmd %d\n", ++ mmc_hostname(host->mmc), ++ (unsigned)intmask, ++ host->cmd->opcode); ++ host->cmd->error = -ETIMEDOUT; ++ tasklet_schedule(&host->finish_tasklet); return; } } -@@ -2436,6 +2834,21 @@ again: +@@ -2436,6 +2844,21 @@ again: } if (intmask & SDHCI_INT_CMD_MASK) { @@ -48465,7 +51530,7 @@ index 2ea429c..8289777 100644 sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK, SDHCI_INT_STATUS); sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK); -@@ -2491,94 +2904,841 @@ out: +@@ -2491,83 +2914,830 @@ out: return result; } @@ -48639,9 +51704,6 @@ index 2ea429c..8289777 100644 - } else { - sdhci_enable_irq_wakeups(host); - enable_irq_wake(host->irq); -- } -- return ret; --} + /* + * set transfer mode + */ @@ -48655,19 +51717,18 @@ index 2ea429c..8289777 100644 + } + if (host->flags & SDHCI_REQ_USE_DMA) + mode |= SDHCI_TRNS_DMA; ++ ++ sdhci_writew(host, mode, SDHCI_TRANSFER_MODE); + } +- return ret; +-} -EXPORT_SYMBOL_GPL(sdhci_suspend_host); -+ sdhci_writew(host, mode, SDHCI_TRANSFER_MODE); -+ } ++ sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT); -int sdhci_resume_host(struct sdhci_host *host) -{ - int ret; -+ sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT); - -- if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { -- if (host->ops->enable_dma) -- host->ops->enable_dma(host); + if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) { + pr_err("%s %s: Unsupported response type!\n", + __func__, mmc_hostname(host->mmc)); @@ -49359,13 +52420,10 @@ index 2ea429c..8289777 100644 + unsigned long flags; + + sdhci_acquire_ownership(host->mmc); -+ -+ if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { -+ if (host->ops->enable_dma) -+ host->ops->enable_dma(host); - } - if (!device_may_wakeup(mmc_dev(host->mmc))) { + if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { + if (host->ops->enable_dma) +@@ -2578,7 +3748,7 @@ int sdhci_resume_host(struct sdhci_host *host) ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, mmc_hostname(host->mmc), host); if (ret) @@ -49374,7 +52432,7 @@ index 2ea429c..8289777 100644 } else { sdhci_disable_irq_wakeups(host); disable_irq_wake(host->irq); -@@ -2596,6 +3756,10 @@ int sdhci_resume_host(struct sdhci_host *host) +@@ -2596,6 +3766,10 @@ int sdhci_resume_host(struct sdhci_host *host) mmiowb(); } @@ -49385,7 +52443,7 @@ index 2ea429c..8289777 100644 ret = mmc_resume_host(host->mmc); sdhci_enable_card_detection(host); -@@ -2606,6 +3770,10 @@ int sdhci_resume_host(struct sdhci_host *host) +@@ -2606,6 +3780,10 @@ int sdhci_resume_host(struct sdhci_host *host) if (host->flags & SDHCI_USING_RETUNING_TIMER) host->flags |= SDHCI_NEEDS_RETUNING; @@ -49396,7 +52454,7 @@ index 2ea429c..8289777 100644 return ret; } -@@ -2630,6 +3798,7 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host) +@@ -2630,6 +3808,7 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host) unsigned long flags; int ret = 0; @@ -49404,7 +52462,7 @@ index 2ea429c..8289777 100644 /* Disable tuning since we are suspending */ if (host->flags & SDHCI_USING_RETUNING_TIMER) { del_timer_sync(&host->tuning_timer); -@@ -2646,6 +3815,7 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host) +@@ -2646,6 +3825,7 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host) host->runtime_suspended = true; spin_unlock_irqrestore(&host->lock, flags); @@ -49412,7 +52470,7 @@ index 2ea429c..8289777 100644 return ret; } EXPORT_SYMBOL_GPL(sdhci_runtime_suspend_host); -@@ -2655,6 +3825,8 @@ int sdhci_runtime_resume_host(struct sdhci_host *host) +@@ -2655,6 +3835,8 @@ int sdhci_runtime_resume_host(struct sdhci_host *host) unsigned long flags; int ret = 0, host_flags = host->flags; @@ -49421,7 +52479,7 @@ index 2ea429c..8289777 100644 if (host_flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { if (host->ops->enable_dma) host->ops->enable_dma(host); -@@ -2692,6 +3864,7 @@ int sdhci_runtime_resume_host(struct sdhci_host *host) +@@ -2692,6 +3874,7 @@ int sdhci_runtime_resume_host(struct sdhci_host *host) spin_unlock_irqrestore(&host->lock, flags); @@ -49429,7 +52487,7 @@ index 2ea429c..8289777 100644 return ret; } EXPORT_SYMBOL_GPL(sdhci_runtime_resume_host); -@@ -2724,6 +3897,36 @@ struct sdhci_host *sdhci_alloc_host(struct device *dev, +@@ -2724,6 +3907,36 @@ struct sdhci_host *sdhci_alloc_host(struct device *dev, EXPORT_SYMBOL_GPL(sdhci_alloc_host); @@ -49466,7 +52524,7 @@ index 2ea429c..8289777 100644 int sdhci_add_host(struct sdhci_host *host) { struct mmc_host *mmc; -@@ -2897,6 +4100,8 @@ int sdhci_add_host(struct sdhci_host *host) +@@ -2897,6 +4110,8 @@ int sdhci_add_host(struct sdhci_host *host) mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23; @@ -49475,7 +52533,7 @@ index 2ea429c..8289777 100644 if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) host->flags |= SDHCI_AUTO_CMD12; -@@ -2989,6 +4194,8 @@ int sdhci_add_host(struct sdhci_host *host) +@@ -2989,6 +4204,8 @@ int sdhci_add_host(struct sdhci_host *host) /* Initial value for re-tuning timer count */ host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >> SDHCI_RETUNING_TIMER_COUNT_SHIFT; @@ -49484,7 +52542,7 @@ index 2ea429c..8289777 100644 /* * In case Re-tuning Timer is not disabled, the actual value of -@@ -3002,17 +4209,10 @@ int sdhci_add_host(struct sdhci_host *host) +@@ -3002,17 +4219,10 @@ int sdhci_add_host(struct sdhci_host *host) SDHCI_RETUNING_MODE_SHIFT; ocr_avail = 0; @@ -49504,7 +52562,7 @@ index 2ea429c..8289777 100644 /* * Voltage range check makes sense only if regulator reports * any voltage value. -@@ -3080,6 +4280,11 @@ int sdhci_add_host(struct sdhci_host *host) +@@ -3080,6 +4290,11 @@ int sdhci_add_host(struct sdhci_host *host) SDHCI_MAX_CURRENT_MULTIPLIER; } @@ -49516,7 +52574,7 @@ index 2ea429c..8289777 100644 mmc->ocr_avail = ocr_avail; mmc->ocr_avail_sdio = ocr_avail; if (host->ocr_avail_sdio) -@@ -3099,8 +4304,6 @@ int sdhci_add_host(struct sdhci_host *host) +@@ -3099,8 +4314,6 @@ int sdhci_add_host(struct sdhci_host *host) return -ENODEV; } @@ -49525,7 +52583,7 @@ index 2ea429c..8289777 100644 /* * Maximum number of segments. Depends on if the hardware * can do scatter/gather or not. -@@ -3181,6 +4384,7 @@ int sdhci_add_host(struct sdhci_host *host) +@@ -3181,6 +4394,7 @@ int sdhci_add_host(struct sdhci_host *host) mmc_hostname(mmc), host->irq, ret); goto untasklet; } @@ -49533,7 +52591,7 @@ index 2ea429c..8289777 100644 sdhci_init(host, 0); -@@ -3189,18 +4393,20 @@ int sdhci_add_host(struct sdhci_host *host) +@@ -3189,18 +4403,20 @@ int sdhci_add_host(struct sdhci_host *host) #endif #ifdef SDHCI_USE_LEDS_CLASS @@ -49566,7 +52624,7 @@ index 2ea429c..8289777 100644 } #endif -@@ -3215,13 +4421,18 @@ int sdhci_add_host(struct sdhci_host *host) +@@ -3215,13 +4431,18 @@ int sdhci_add_host(struct sdhci_host *host) sdhci_enable_card_detection(host); @@ -49588,7 +52646,7 @@ index 2ea429c..8289777 100644 #endif untasklet: tasklet_kill(&host->card_tasklet); -@@ -3257,7 +4468,8 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) +@@ -3257,7 +4478,8 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) mmc_remove_host(host->mmc); #ifdef SDHCI_USE_LEDS_CLASS @@ -49814,7 +52872,7 @@ index df4655c..02d48ec 100644 * If arch hasn't set it explicitly yet, use the CLS * value shared by all PCI devices. If there's a diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig -index 8577261..a1df080 100644 +index 8577261..d42f5ef 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -651,6 +651,20 @@ config INTEL_SCU_IPC @@ -49838,7 +52896,7 @@ index 8577261..a1df080 100644 config INTEL_SCU_IPC_UTIL tristate "Intel SCU IPC utility driver" -@@ -789,4 +803,13 @@ config PVPANIC +@@ -789,4 +803,30 @@ config PVPANIC a paravirtualized device provided by QEMU; it lets a virtual machine (guest) communicate panic events to the host. @@ -49850,10 +52908,27 @@ index 8577261..a1df080 100644 + This driver builds the SCU Flis Access Sysfs Interfaces. + We could read write the flis address and configure the + pin pull up/down using these interfaces. ++ ++config INTEL_PSH_IPC ++ bool "Intel PSH IPC Support" ++ depends on X86_INTEL_MID ++ ---help--- ++ PSH(Platform Services Hub) is a low frequence IA core on Tangier Platform, ++ whose power consumption is quite low. PSH runs RTOS software inside itself, ++ which independently controls and collects sensor data, pre-processes the data, ++ and communicates with Atom. Thus ATOM side could be put into low power mode ++ with more time, while all the sensor data are collected without any lost. ++ ++ PSH IPC is used as a bridge for OS sensor service to control and access PSH ++ sensors communications between kernel and PSH. This is not needed for PC-type ++ machines. ++ ++ Say Y here to get Intel PSH IPC support. ++ + endif # X86_PLATFORM_DEVICES diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile -index ef0ec74..c075bb2 100644 +index ef0ec74..7d82ebf 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -36,9 +36,9 @@ obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o @@ -49868,6 +52943,12 @@ index ef0ec74..c075bb2 100644 obj-$(CONFIG_INTEL_IPS) += intel_ips.o obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o +@@ -53,3 +53,5 @@ obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o + obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o + + obj-$(CONFIG_PVPANIC) += pvpanic.o ++ ++obj-$(CONFIG_INTEL_PSH_IPC) += intel_psh_ipc.o diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c index f59683a..669a254 100644 --- a/drivers/platform/x86/intel_mid_powerbtn.c @@ -50785,6 +53866,649 @@ index 6f4b728..c2d271a 100644 #include #include #include +diff --git a/drivers/platform/x86/intel_psh_ipc.c b/drivers/platform/x86/intel_psh_ipc.c +new file mode 100644 +index 0000000..12e8bf1 +--- /dev/null ++++ b/drivers/platform/x86/intel_psh_ipc.c +@@ -0,0 +1,637 @@ ++/* ++ * intel_psh_ipc.c: Driver for the Intel PSH IPC mechanism ++ * ++ * (C) Copyright 2012 Intel Corporation ++ * Author: Yang Bin (bin.yang@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. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define PSH_ERR(fmt, arg...) dev_err(&ipc_ctrl.pdev->dev, fmt, ##arg) ++#define PSH_DBG(fmt, arg...) dev_dbg(&ipc_ctrl.pdev->dev, fmt, ##arg) ++ ++#define STATUS_PSH2IA(x) (1 << ((x) + 6)) ++#define FLAG_BIND (1 << 0) ++ ++#define PIMR_ADDR(x) (&ipc_ctrl.psh_regs->psh_regs_b_step.pimr##x) ++ ++#define PSH_REG_ADDR(x) (&ipc_ctrl.psh_regs->psh_regs_b_step.x) ++ ++#define PSH_CH_HANDLE(x) (ipc_ctrl.channel_handle[x]) ++#define PSH_CH_DATA(x) (ipc_ctrl.channel_data[x]) ++#define PSH_CH_FLAG(x) (ipc_ctrl.flags[x]) ++ ++/* PSH registers */ ++union psh_registers { ++ /* reg mem map A */ ++ struct { ++ u32 csr; /* 00h */ ++ u32 res1; /* padding */ ++ u32 pisr; /* 08h */ ++ u32 pimr0; /* 0Ch */ ++ u32 pimr1; /* 10h */ ++ u32 pimr2; /* 14h */ ++ u32 pimr3; /* 18h */ ++ u32 pmctl; /* 1Ch */ ++ u32 pmstat; /* 20h */ ++ u32 res2; /* padding */ ++ struct psh_msg ia2psh[NUM_IA2PSH_IPC];/* 28h ~ 44h + 3 */ ++ struct psh_msg cry2psh;/* 48h ~ 4Ch + 3 */ ++ struct psh_msg scu2psh;/* 50h ~ 54h + 3 */ ++ u32 res3[2];/* padding */ ++ struct psh_msg psh2ia[NUM_PSH2IA_IPC];/* 60h ~ 7Ch + 3 */ ++ struct psh_msg psh2cry;/* 80h ~ 84h + 3 */ ++ struct psh_msg psh2scu;/* 88h */ ++ u32 msi_dir;/* 90h */ ++ u32 res4[3]; ++ u32 scratchpad[2];/* A0 */ ++ } __packed psh_regs_a_step; ++ /* reg mem map B */ ++ struct { ++ u32 pimr0; /* 00h */ ++ u32 csr; /* 04h */ ++ u32 pmctl; /* 08h */ ++ u32 pmstat; /* 0Ch */ ++ u32 psh_msi_direct; /* 10h */ ++ u32 res1[59]; /* 14h ~ FCh + 3, padding */ ++ u32 pimr3; /* 100h */ ++ struct psh_msg scu2psh; /* 104h ~ 108h + 3 */ ++ struct psh_msg psh2scu; /* 10Ch ~ 110h + 3 */ ++ u32 res2[187]; /* 114h ~ 3FCh + 3, padding */ ++ u32 pisr; /* 400h */ ++ u32 scratchpad[2]; /* 404h ~ 407h */ ++ u32 res3[61]; /* 40Ch ~ 4FCh + 3, padding */ ++ u32 pimr1; /* 500h */ ++ struct psh_msg ia2psh[NUM_IA2PSH_IPC]; /* 504h ~ 520h + 3 */ ++ struct psh_msg psh2ia[NUM_PSH2IA_IPC]; /* 524h ~ 540h + 3 */ ++ u32 res4[175]; /* 544h ~ 7FCh + 3, padding */ ++ u32 pimr2; /* 800h */ ++ struct psh_msg cry2psh; /* 804h ~ 808h + 3 */ ++ struct psh_msg psh2cry; /* 80Ch ~ 810h + 3 */ ++ } __packed psh_regs_b_step; ++} __packed; ++ ++static struct ipc_controller_t { ++ int reg_map; ++ int initialized; ++ struct pci_dev *pdev; ++ spinlock_t lock; ++ int flags[NUM_ALL_CH]; ++ union psh_registers *psh_regs; ++ struct semaphore ch_lock[NUM_ALL_CH]; ++ struct mutex psh_mutex; ++ psh_channel_handle_t channel_handle[NUM_PSH2IA_IPC]; ++ void *channel_data[NUM_PSH2IA_IPC]; ++} ipc_ctrl; ++ ++ ++/** ++ * intel_ia2psh_command - send IA to PSH command ++ * Send ia2psh command and return psh message and status ++ * ++ * @in: input psh message ++ * @out: output psh message ++ * @ch: psh channel ++ * @timeout: timeout for polling busy bit, in us ++ */ ++int intel_ia2psh_command(struct psh_msg *in, struct psh_msg *out, ++ int ch, int timeout) ++{ ++ int ret = 0; ++ u32 status; ++ ++ might_sleep(); ++ ++ if (!ipc_ctrl.initialized) ++ return -ENODEV; ++ ++ if (ch < PSH_SEND_CH0 || ch > PSH_SEND_CH0 + NUM_IA2PSH_IPC - 1 ++ || in == NULL) ++ return -EINVAL; ++ ++ if (!in || in->msg & CHANNEL_BUSY) ++ return -EINVAL; ++ ++ pm_runtime_get_sync(&ipc_ctrl.pdev->dev); ++ down(&ipc_ctrl.ch_lock[ch]); ++ ++ in->msg |= CHANNEL_BUSY; ++ /* Check if channel is ready for IA sending command */ ++ ++ if (readl(PSH_REG_ADDR(ia2psh[ch].msg)) & CHANNEL_BUSY) { ++ ret = -EBUSY; ++ goto end; ++ } ++ ++ writel(in->param, PSH_REG_ADDR(ia2psh[ch].param)); ++ writel(in->msg, PSH_REG_ADDR(ia2psh[ch].msg)); ++ ++ /* Input timeout is zero, do not check channel status */ ++ if (timeout == 0) ++ goto end; ++ ++ /* Input timeout is nonzero, check channel status */ ++ while (((status = readl(PSH_REG_ADDR(ia2psh[ch].msg))) & CHANNEL_BUSY) ++ && timeout) { ++ usleep_range(100, 101); ++ timeout -= 100; ++ } ++ ++ if (timeout <= 0) { ++ ret = -ETIMEDOUT; ++ PSH_ERR("ia2psh channel %d is always busy!\n", ch); ++ goto end; ++ } else { ++ if (out == NULL) ++ goto end; ++ ++ out->param = readl(PSH_REG_ADDR(ia2psh[ch].param)); ++ out->msg = status; ++ } ++ ++end: ++ up(&ipc_ctrl.ch_lock[ch]); ++ pm_runtime_put(&ipc_ctrl.pdev->dev); ++ ++ return ret; ++} ++EXPORT_SYMBOL(intel_ia2psh_command); ++ ++/** ++ * intel_psh_ipc_bind - bind a handler to a psh channel ++ * ++ * @ch: psh channel ++ * @handle: handle function called when IA received psh interrupt ++ * @data: data passed to handle ++ */ ++int intel_psh_ipc_bind(int ch, psh_channel_handle_t handle, void *data) ++{ ++ unsigned long flags; ++ ++ if (!ipc_ctrl.initialized) ++ return -ENODEV; ++ ++ if (!handle || ch < PSH_RECV_CH0 ++ || ch > PSH_RECV_CH0 + NUM_PSH2IA_IPC - 1) ++ return -EINVAL; ++ ++ mutex_lock(&ipc_ctrl.psh_mutex); ++ down(&ipc_ctrl.ch_lock[ch]); ++ if (PSH_CH_HANDLE(ch - PSH_RECV_CH0) != NULL) { ++ up(&ipc_ctrl.ch_lock[ch]); ++ mutex_unlock(&ipc_ctrl.psh_mutex); ++ return -EBUSY; ++ } else { ++ PSH_CH_DATA(ch - PSH_RECV_CH0) = data; ++ PSH_CH_HANDLE(ch - PSH_RECV_CH0) = handle; ++ } ++ up(&ipc_ctrl.ch_lock[ch]); ++ ++ pm_runtime_get_sync(&ipc_ctrl.pdev->dev); ++ spin_lock_irqsave(&ipc_ctrl.lock, flags); ++ PSH_CH_FLAG(ch) |= FLAG_BIND; ++ writel(readl(PIMR_ADDR(1)) | (1 << (ch - PSH_RECV_CH0)), PIMR_ADDR(1)); ++ spin_unlock_irqrestore(&ipc_ctrl.lock, flags); ++ pm_runtime_put(&ipc_ctrl.pdev->dev); ++ mutex_unlock(&ipc_ctrl.psh_mutex); ++ ++ return 0; ++} ++EXPORT_SYMBOL(intel_psh_ipc_bind); ++ ++/** ++ * intel_psh_ipc_unbind - unbind a handler to a psh channel ++ * ++ * @ch: psh channel ++ */ ++void intel_psh_ipc_unbind(int ch) ++{ ++ unsigned long flags; ++ ++ if (!ipc_ctrl.initialized) ++ return; ++ ++ if (ch < PSH_RECV_CH0 || ch > PSH_RECV_CH0 + NUM_PSH2IA_IPC - 1) ++ return; ++ ++ if (!(PSH_CH_FLAG(ch) & FLAG_BIND)) ++ return; ++ ++ mutex_lock(&ipc_ctrl.psh_mutex); ++ pm_runtime_get_sync(&ipc_ctrl.pdev->dev); ++ spin_lock_irqsave(&ipc_ctrl.lock, flags); ++ PSH_CH_FLAG(ch) &= ~FLAG_BIND; ++ writel(readl(PIMR_ADDR(1)) & (~(1 << (ch - PSH_RECV_CH0))), ++ PIMR_ADDR(1)); ++ spin_unlock_irqrestore(&ipc_ctrl.lock, flags); ++ pm_runtime_put(&ipc_ctrl.pdev->dev); ++ ++ down(&ipc_ctrl.ch_lock[ch]); ++ PSH_CH_HANDLE(ch - PSH_RECV_CH0) = NULL; ++ up(&ipc_ctrl.ch_lock[ch]); ++ mutex_unlock(&ipc_ctrl.psh_mutex); ++} ++EXPORT_SYMBOL(intel_psh_ipc_unbind); ++ ++void intel_psh_ipc_disable_irq(void) ++{ ++ disable_irq(ipc_ctrl.pdev->irq); ++} ++EXPORT_SYMBOL(intel_psh_ipc_disable_irq); ++ ++void intel_psh_ipc_enable_irq(void) ++{ ++ enable_irq(ipc_ctrl.pdev->irq); ++} ++EXPORT_SYMBOL(intel_psh_ipc_enable_irq); ++ ++static void psh_recv_handle(int i) ++{ ++ int msg, param; ++ ++ down(&ipc_ctrl.ch_lock[i + PSH_RECV_CH0]); ++ ++ msg = readl(PSH_REG_ADDR(psh2ia[i].msg)) & (~CHANNEL_BUSY); ++ param = readl(PSH_REG_ADDR(psh2ia[i].param)); ++ ++ if (PSH_CH_HANDLE(i) == NULL) { ++ PSH_ERR("Ignore message from channel %d\n", i+PSH_RECV_CH0); ++ goto end; ++ } ++ ++ /* write back to clear the busy bit */ ++ writel(msg, PSH_REG_ADDR(psh2ia[i].msg)); ++ PSH_CH_HANDLE(i)(msg, param, PSH_CH_DATA(i)); ++end: ++ up(&ipc_ctrl.ch_lock[i+PSH_RECV_CH0]); ++} ++ ++static irqreturn_t psh_ipc_irq(int irq, void *data) ++{ ++ int i; ++ u32 status; ++ ++ pm_runtime_get_sync(&ipc_ctrl.pdev->dev); ++ status = readl(PSH_REG_ADDR(pisr)); ++ ++ for (i = 0; i < NUM_PSH2IA_IPC; i++) { ++ if (status & STATUS_PSH2IA(i)) ++ psh_recv_handle(i); ++ } ++ ++ pm_runtime_put(&ipc_ctrl.pdev->dev); ++ return IRQ_HANDLED; ++} ++ ++static void psh_regs_dump(void) ++{ ++ int i; ++ ++ pm_runtime_get_sync(&ipc_ctrl.pdev->dev); ++ PSH_ERR("\n<-------------start------------>\n"); ++ ++ PSH_ERR("csr:\t%#x\n", readl(PSH_REG_ADDR(csr))); ++ PSH_ERR("pisr:\t%#x\n", readl(PSH_REG_ADDR(pisr))); ++ ++ PSH_ERR("pimr0:\t%#x\n", readl(PIMR_ADDR(0))); ++ PSH_ERR("pimr1:\t%#x\n", readl(PIMR_ADDR(1))); ++ PSH_ERR("pimr2:\t%#x\n", readl(PIMR_ADDR(2))); ++ PSH_ERR("pimr3:\t%#x\n", readl(PIMR_ADDR(3))); ++ ++ PSH_ERR("pmctl:\t%#x\n", readl(PSH_REG_ADDR(pmctl))); ++ PSH_ERR("pmstat:\t%#x\n", readl(PSH_REG_ADDR(pmstat))); ++ PSH_ERR("scratchpad0:\t%#x\n", readl(PSH_REG_ADDR(scratchpad[0]))); ++ PSH_ERR("scratchpad1:\t%#x\n", readl(PSH_REG_ADDR(scratchpad[1]))); ++ ++ for (i = 0; i < NUM_IA2PSH_IPC; i++) { ++ PSH_ERR("ia2psh[%d].msg:\t%#x\n", i, ++ readl(PSH_REG_ADDR(ia2psh[i].msg))); ++ PSH_ERR("ia2psh[%d].param:\t%#x\n", i, ++ readl(PSH_REG_ADDR(ia2psh[i].param))); ++ } ++ ++ PSH_ERR("cry2psh.msg:\t%#x\n", readl(PSH_REG_ADDR(cry2psh.msg))); ++ PSH_ERR("cry2psh.param:\t%#x\n", readl(PSH_REG_ADDR(cry2psh.param))); ++ PSH_ERR("scu2psh.msg:\t%#x\n", readl(PSH_REG_ADDR(scu2psh.msg))); ++ PSH_ERR("scu2psh.param:\t%#x\n", readl(PSH_REG_ADDR(scu2psh.param))); ++ ++ for (i = 0; i < NUM_PSH2IA_IPC; i++) { ++ PSH_ERR("psh2ia[%d].msg:\t%#x\n", i, ++ readl(PSH_REG_ADDR(psh2ia[i].msg))); ++ PSH_ERR("psh2ia[%d].param:\t%#x\n", i, ++ readl(PSH_REG_ADDR(psh2ia[i].param))); ++ } ++ ++ PSH_ERR("psh2cry.msg:\t%#x\n", readl(PSH_REG_ADDR(psh2cry.msg))); ++ PSH_ERR("psh2cry.param:\t%#x\n", readl(PSH_REG_ADDR(psh2cry.param))); ++ ++ PSH_ERR("\n<-------------end------------>\n"); ++ pm_runtime_put(&ipc_ctrl.pdev->dev); ++} ++ ++static struct psh_msg psh_dbg_msg; ++static int psh_ch; ++ ++static ssize_t psh_msg_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ return snprintf(buf, PAGE_SIZE, ++ "\nLast ia2psh command with msg: %#x\nparam: %#x\n", ++ psh_dbg_msg.msg, psh_dbg_msg.param); ++} ++ ++static ssize_t psh_msg_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ u32 msg, param; ++ ++ memset(&psh_dbg_msg, 0, sizeof(psh_dbg_msg)); ++ ++ ret = sscanf(buf, "%x %x", &msg, ¶m); ++ if (ret != 2) { ++ PSH_ERR("Input two arguments as psh msg and param\n"); ++ return -EINVAL; ++ } ++ ++ psh_dbg_msg.msg = msg; ++ psh_dbg_msg.param = param; ++ ++ return size; ++} ++ ++static ssize_t psh_ch_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ return snprintf(buf, PAGE_SIZE, ++ "\nLast psh channel: %d\n", psh_ch); ++} ++ ++static ssize_t psh_ch_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ ++ ret = sscanf(buf, "%d", &psh_ch); ++ if (ret != 1) { ++ PSH_ERR("Input one argument as psh channel\n"); ++ return -EINVAL; ++ } ++ ++ return size; ++} ++ ++static ssize_t psh_send_cmd_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int psh_dbg_err; ++ struct psh_msg out_msg; ++ ++ memset(&out_msg, 0, sizeof(out_msg)); ++ ++ psh_dbg_err = intel_ia2psh_command(&psh_dbg_msg, &out_msg, ++ psh_ch, 3000000); ++ if (psh_dbg_err) { ++ PSH_ERR("Send ia2psh command failed, err %d\n", psh_dbg_err); ++ psh_regs_dump(); ++ return psh_dbg_err; ++ } ++ ++ return size; ++} ++ ++static DEVICE_ATTR(psh_msg, S_IRUGO | S_IWUSR, psh_msg_show, psh_msg_store); ++static DEVICE_ATTR(psh_ch, S_IRUGO | S_IWUSR, psh_ch_show, psh_ch_store); ++static DEVICE_ATTR(ia2psh_cmd, S_IWUSR, NULL, psh_send_cmd_store); ++ ++static struct attribute *psh_attrs[] = { ++ &dev_attr_psh_msg.attr, ++ &dev_attr_psh_ch.attr, ++ &dev_attr_ia2psh_cmd.attr, ++ NULL, ++}; ++ ++static struct attribute_group psh_attr_group = { ++ .name = "psh_debug", ++ .attrs = psh_attrs, ++}; ++ ++static int intel_psh_debug_sysfs_create(struct pci_dev *pdev) ++{ ++ return sysfs_create_group(&pdev->dev.kobj, &psh_attr_group); ++} ++ ++static void pmic_sysfs_remove(struct pci_dev *pdev) ++{ ++ sysfs_remove_group(&pdev->dev.kobj, &psh_attr_group); ++} ++ ++#ifdef CONFIG_PM ++static int psh_ipc_suspend_noirq(struct device *dev) ++{ ++ int i; ++ int ret = 0; ++ ++ for (i = 0; i < NUM_ALL_CH; i++) { ++ if (down_trylock(&ipc_ctrl.ch_lock[i])) { ++ ret = -EBUSY; ++ break; ++ } ++ } ++ ++ if (ret) { ++ for (; i > 0; i--) ++ up(&ipc_ctrl.ch_lock[i - 1]); ++ } ++ ++ return ret; ++} ++ ++static int psh_ipc_resume_noirq(struct device *dev) ++{ ++ int i; ++ ++ for (i = 0; i < NUM_ALL_CH; i++) ++ up(&ipc_ctrl.ch_lock[i]); ++ ++ return 0; ++} ++ ++#else ++ ++#define psh_ipc_suspend_noirq NULL ++#define psh_ipc_resume_noirq NULL ++ ++#endif ++ ++#ifdef CONFIG_PM_RUNTIME ++static int psh_ipc_runtime_suspend(struct device *dev) ++{ ++ dev_dbg(dev, "runtime suspend called\n"); ++ return 0; ++} ++ ++static int psh_ipc_runtime_resume(struct device *dev) ++{ ++ dev_dbg(dev, "runtime resume called\n"); ++ return 0; ++} ++ ++#else ++ ++#define psh_ipc_runtime_suspend NULL ++#define psh_ipc_runtime_resume NULL ++ ++#endif ++ ++static int psh_ipc_probe(struct pci_dev *pdev, const struct pci_device_id *id) ++{ ++ int i, ret; ++ unsigned long start, len; ++ ++ ipc_ctrl.pdev = pci_dev_get(pdev); ++ ret = pci_enable_device(pdev); ++ if (ret) ++ goto err1; ++ ++ start = pci_resource_start(pdev, 0); ++ len = pci_resource_len(pdev, 0); ++ if (!start || !len) { ++ ret = -ENODEV; ++ goto err1; ++ } ++ ++ ret = pci_request_regions(pdev, "intel_psh_ipc"); ++ if (ret) ++ goto err1; ++ ++ switch (intel_mid_identify_cpu()) { ++ case INTEL_MID_CPU_CHIP_TANGIER: ++ ipc_ctrl.reg_map = 1; ++ break; ++ case INTEL_MID_CPU_CHIP_ANNIEDALE: ++ ipc_ctrl.reg_map = 1; ++ break; ++ default: ++ dev_err(&pdev->dev, "error register map\n"); ++ ret = -EINVAL; ++ goto err2; ++ break; ++ } ++ ++ ipc_ctrl.psh_regs = (union psh_registers *)ioremap_nocache(start, len); ++ if (!ipc_ctrl.psh_regs) { ++ ret = -ENOMEM; ++ goto err2; ++ } ++ ++ ret = request_threaded_irq(pdev->irq, NULL, psh_ipc_irq, IRQF_ONESHOT, ++ "intel_psh_ipc", NULL); ++ if (ret) { ++ dev_err(&pdev->dev, "Unable to register irq %d\n", pdev->irq); ++ goto err3; ++ } ++ ++ irq_set_irq_wake(pdev->irq, 1); ++ ++ spin_lock_init(&ipc_ctrl.lock); ++ mutex_init(&ipc_ctrl.psh_mutex); ++ ++ for (i = 0; i < NUM_ALL_CH; i++) ++ sema_init(&ipc_ctrl.ch_lock[i], 1); ++ ++ intel_psh_devices_create(); ++ ++ intel_psh_debug_sysfs_create(pdev); ++ ++ ipc_ctrl.initialized = 1; ++ ++ pm_runtime_put_noidle(&pdev->dev); ++ pm_runtime_allow(&pdev->dev); ++ ++ return 0; ++ ++err3: ++ iounmap(ipc_ctrl.psh_regs); ++err2: ++ pci_release_regions(pdev); ++err1: ++ pci_dev_put(pdev); ++ ++ return ret; ++} ++ ++static void psh_ipc_remove(struct pci_dev *pdev) ++{ ++ pm_runtime_forbid(&pdev->dev); ++ pm_runtime_get_noresume(&pdev->dev); ++ free_irq(pdev->irq, NULL); ++ iounmap(ipc_ctrl.psh_regs); ++ pci_release_regions(pdev); ++ pci_dev_put(pdev); ++ intel_psh_devices_destroy(); ++ pmic_sysfs_remove(pdev); ++ ipc_ctrl.initialized = 0; ++} ++ ++static const struct dev_pm_ops psh_ipc_drv_pm_ops = { ++ .suspend_noirq = psh_ipc_suspend_noirq, ++ .resume_noirq = psh_ipc_resume_noirq, ++ .runtime_suspend = psh_ipc_runtime_suspend, ++ .runtime_resume = psh_ipc_runtime_resume, ++}; ++ ++static DEFINE_PCI_DEVICE_TABLE(pci_ids) = { ++ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x11a3)}, ++ { 0,} ++}; ++MODULE_DEVICE_TABLE(pci, pci_ids); ++ ++static struct pci_driver psh_ipc_driver = { ++ .name = "intel_psh_ipc", ++ .driver = { ++ .pm = &psh_ipc_drv_pm_ops, ++ }, ++ .id_table = pci_ids, ++ .probe = psh_ipc_probe, ++ .remove = psh_ipc_remove, ++}; ++ ++static int __init psh_ipc_init(void) ++{ ++ return pci_register_driver(&psh_ipc_driver); ++} ++ ++static void __exit psh_ipc_exit(void) ++{ ++ pci_unregister_driver(&psh_ipc_driver); ++} ++ ++MODULE_AUTHOR("bin.yang@intel.com"); ++MODULE_DESCRIPTION("Intel PSH IPC driver"); ++MODULE_LICENSE("GPL v2"); ++ ++fs_initcall(psh_ipc_init); ++module_exit(psh_ipc_exit); diff --git a/drivers/platform/x86/intel_scu_flis.c b/drivers/platform/x86/intel_scu_flis.c new file mode 100644 index 0000000..4dc3598 @@ -63668,10 +67392,10 @@ index 32221cb..9e9f5b8 100644 diff --git a/drivers/pwm/pwm-intel-mid.c b/drivers/pwm/pwm-intel-mid.c new file mode 100644 -index 0000000..5564a1b +index 0000000..7897607 --- /dev/null +++ b/drivers/pwm/pwm-intel-mid.c -@@ -0,0 +1,405 @@ +@@ -0,0 +1,404 @@ +/* + * pwm-intel-mid.c: Driver for PWM on Intel MID platform + * @@ -63748,15 +67472,9 @@ index 0000000..5564a1b + u64 on_time_divisor; + + /* Calculate and set on time divisor */ -+ if (duty_cycle == period) { -+ on_time_divisor = 1UL; -+ } else { -+ on_time_divisor = duty_cycle * -+ (u64)(PWM_COMPARE_UNIT_SIZE - 1UL); -+ do_div(on_time_divisor, period); -+ on_time_divisor = PWM_COMPARE_UNIT_SIZE - -+ on_time_divisor - 1UL; -+ } ++ on_time_divisor = duty_cycle * (u64)(PWM_COMPARE_UNIT_SIZE - 1UL); ++ do_div(on_time_divisor, period); ++ on_time_divisor = PWM_COMPARE_UNIT_SIZE - on_time_divisor - 1UL; + + pwmctrl->full = readl(reg); + pwmctrl->part.on_time_divisor = on_time_divisor; @@ -63841,6 +67559,11 @@ index 0000000..5564a1b + fraction -= numerator; + } + } ++ /* If both the values are 0, the output will be somehow not correct. ++ * So if it happens, change the fraction to 1. ++ */ ++ if ((0 == base_unit_fraction) && (0 == base_unit_integer)) ++ base_unit_fraction = 1UL; + + pwmctrl->full = readl(reg); + pwmctrl->part.base_unit_int = (u32)base_unit_integer; @@ -65173,10 +68896,10 @@ index 0000000..bfe6f6c +extern void intel_scu_ipc_unlock(void); diff --git a/drivers/remoteproc/intel_mid_rproc_scu.c b/drivers/remoteproc/intel_mid_rproc_scu.c new file mode 100644 -index 0000000..fd5023b +index 0000000..53a1cdc --- /dev/null +++ b/drivers/remoteproc/intel_mid_rproc_scu.c -@@ -0,0 +1,438 @@ +@@ -0,0 +1,441 @@ +/* + * INTEL MID Remote Processor - SCU driver + * @@ -65436,6 +69159,7 @@ index 0000000..fd5023b + struct intel_mid_rproc *iproc; + struct rproc_vdev *rvdev; + struct device *dev = rproc->dev.parent; ++ static unsigned long ns_info_all_received; + + iproc = (struct intel_mid_rproc *)rproc->priv; + @@ -65447,19 +69171,21 @@ index 0000000..fd5023b + + switch (idx) { + case RX_VRING: -+ if (iproc->ns_enabled && -+ !list_is_last(&iproc->ns_info->node, &nslist->list)) { ++ if (iproc->ns_enabled && !ns_info_all_received) { ++ /* push messages with ns_info for ALL available ++ name services in the list (nslist) into ++ rx buffers. */ + list_for_each_entry_continue(iproc->ns_info, + &nslist->list, node) { + ret = intel_mid_rproc_ns_handle(iproc, -+ iproc->ns_info); ++ iproc->ns_info); + if (ret) { + dev_err(dev, "ns handle error\n"); + return; + } -+ break; + } + ++ ns_info_all_received = 1; + intel_mid_rproc_vq_interrupt(rproc, vqid); + } + break; @@ -66650,10 +70376,10 @@ index 33f9c09..1ab0e90 100644 obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o diff --git a/drivers/spi/intel_mid_ssp_spi.c b/drivers/spi/intel_mid_ssp_spi.c new file mode 100644 -index 0000000..938180e +index 0000000..d790620 --- /dev/null +++ b/drivers/spi/intel_mid_ssp_spi.c -@@ -0,0 +1,1751 @@ +@@ -0,0 +1,1787 @@ +/* + * intel_mid_ssp_spi.c + * This driver supports Bulverde SSP core used on Intel MID platforms @@ -67384,7 +71110,7 @@ index 0000000..938180e +#endif + + if (sspc->cs_control) -+ sspc->cs_control(CS_DEASSERT); ++ sspc->cs_control(!sspc->cs_assert); + + dev_dbg(dev, "End of transfer. SSSR:%08X\n", read_SSSR(reg)); + msg = sspc->cur_msg; @@ -67562,9 +71288,9 @@ index 0000000..938180e + +static unsigned int ssp_get_clk_div(struct ssp_drv_context *sspc, int speed) +{ -+ /* The clock divider shall stay between 3 and 4095 as specified in TRM. */ + if (sspc->quirks & QUIRKS_PLATFORM_MRFL) -+ return clamp(25000000 / speed - 1, 3, 4095); ++ /* The clock divider shall stay between 0 and 4095. */ ++ return clamp(25000000 / speed - 1, 0, 4095); + else + return clamp(100000000 / speed - 1, 3, 4095); +} @@ -67607,7 +71333,7 @@ index 0000000..938180e + u32 cr0, saved_cr0, cr1, saved_cr1; + struct device *dev = &sspc->pdev->dev; + struct spi_message *msg = sspc->cur_msg; -+ u32 clk_div, saved_speed_hz; ++ u32 clk_div, saved_speed_hz, speed_hz; + u8 dma_enabled; + u32 timeout; + u8 chip_select; @@ -67779,9 +71505,11 @@ index 0000000..938180e + + /* recalculate the frequency for each transfer */ + if (transfer->speed_hz) -+ clk_div = ssp_get_clk_div(sspc, transfer->speed_hz); ++ speed_hz = transfer->speed_hz; + else -+ clk_div = ssp_get_clk_div(sspc, saved_speed_hz); ++ speed_hz = saved_speed_hz; ++ ++ clk_div = ssp_get_clk_div(sspc, speed_hz); + + cr0 &= ~SSCR0_SCR; + cr0 |= (clk_div & 0xFFF) << 8; @@ -67792,6 +71520,13 @@ index 0000000..938180e + (sspc->quirks & QUIRKS_BIT_BANGING))) { + start_bitbanging(sspc); + } else { ++ ++ /* if speed is higher than 6.25Mhz, enable clock delay */ ++ if (speed_hz > 6250000) ++ write_SSCR2((read_SSCR2(reg) | SSCR2_CLK_DEL_EN), reg); ++ else ++ write_SSCR2((read_SSCR2(reg) & ~SSCR2_CLK_DEL_EN), reg); ++ + /* (re)start the SSP */ + if (ssp_timing_wr) { + dev_dbg(dev, "original cr0 before reset:%x", @@ -67809,7 +71544,7 @@ index 0000000..938180e + } + + if (sspc->cs_control) -+ sspc->cs_control(CS_ASSERT); ++ sspc->cs_control(sspc->cs_assert); + + if (likely(dma_enabled)) { + if (unlikely(sspc->quirks & QUIRKS_USE_PM_QOS)) @@ -67824,7 +71559,7 @@ index 0000000..938180e + if (list_is_last(&transfer->transfer_list, &msg->transfers) + || sspc->cs_change) { + if (sspc->cs_control) -+ sspc->cs_control(CS_DEASSERT); ++ sspc->cs_control(!sspc->cs_assert); + } + + } /* end of list_for_each_entry */ @@ -67863,7 +71598,8 @@ index 0000000..938180e + sspc->cur_msg = NULL; + } + spin_unlock_irqrestore(&sspc->lock, flags); -+ pm_runtime_put(dev); ++ pm_runtime_mark_last_busy(dev); ++ pm_runtime_put_autosuspend(dev); +} + +/** @@ -67914,6 +71650,26 @@ index 0000000..938180e + /* chip_info isn't always needed */ + chip->cr1 = 0; + if (chip_info) { ++ /* If user requested CS Active High need to verify that there ++ * is no transfer pending. If this is the case, kindly fail. */ ++ if ((spi->mode & SPI_CS_HIGH) != sspc->cs_assert) { ++ if (sspc->cur_msg) { ++ dev_err(&spi->dev, "message pending... Failing\n"); ++ /* A message is currently in transfer. Do not toggle CS */ ++ spin_unlock_irqrestore(&sspc->lock, flags); ++ return -EAGAIN; ++ } ++ if (!chip_info->cs_control) { ++ /* unable to control cs by hand */ ++ dev_err(&spi->dev, ++ "This CS does not support SPI_CS_HIGH flag\n"); ++ spin_unlock_irqrestore(&sspc->lock, flags); ++ return -EINVAL; ++ } ++ sspc->cs_assert = spi->mode & SPI_CS_HIGH; ++ chip_info->cs_control(!sspc->cs_assert); ++ } ++ + burst_size = chip_info->burst_size; + if (burst_size > IMSS_FIFO_BURST_8) + burst_size = DFLT_FIFO_BURST_SIZE; @@ -68113,7 +71869,7 @@ index 0000000..938180e + if (ssp_cfg_is_spi_slave(ssp_cfg)) + sspc->quirks |= QUIRKS_SPI_SLAVE_CLOCK_MODE; + -+ master->mode_bits = SPI_CPOL | SPI_CPHA; ++ master->mode_bits = SPI_CS_HIGH | SPI_CPOL | SPI_CPHA; + master->bus_num = ssp_cfg_get_spi_bus_nb(ssp_cfg); + master->num_chipselect = 4; + master->cleanup = cleanup; @@ -68214,6 +71970,12 @@ index 0000000..938180e + pm_qos_add_request(&sspc->pm_qos_req, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + ++ pm_runtime_set_autosuspend_delay(&pdev->dev, 25); ++ pm_runtime_use_autosuspend(&pdev->dev); ++ pm_runtime_set_active(&pdev->dev); ++ pm_runtime_enable(&pdev->dev); ++ if (!pm_runtime_enabled(&pdev->dev)) ++ dev_err(&pdev->dev, "spi runtime pm not enabled!\n"); + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_allow(&pdev->dev); + @@ -95921,7 +99683,7 @@ index 88bf0f0..2a50d36 100644 enum iio_modifier { diff --git a/include/linux/intel_mid_dma.h b/include/linux/intel_mid_dma.h -index 10496bd..374514e 100644 +index 10496bd..d7f2997 100644 --- a/include/linux/intel_mid_dma.h +++ b/include/linux/intel_mid_dma.h @@ -29,6 +29,10 @@ @@ -95939,7 +99701,7 @@ index 10496bd..374514e 100644 struct dma_slave_config dma_slave; }; -+struct device *intel_mid_get_acpi_dma(void); ++struct device *intel_mid_get_acpi_dma(const char *hid); +dma_addr_t intel_dma_get_src_addr(struct dma_chan *chan); +dma_addr_t intel_dma_get_dst_addr(struct dma_chan *chan); #endif /*__INTEL_MID_DMA_H__*/ @@ -96370,8 +100132,78 @@ index 439a7a6..5121763 100644 }; struct intel_msic; +diff --git a/include/linux/mfd/wm8994/pdata.h b/include/linux/mfd/wm8994/pdata.h +index 68e7765..9e96ca7 100644 +--- a/include/linux/mfd/wm8994/pdata.h ++++ b/include/linux/mfd/wm8994/pdata.h +@@ -130,6 +130,12 @@ struct wm8958_micd_rate { + int rate; + }; + ++struct wm8958_custom_config { ++ int format; ++ int rate; ++ int channels; ++}; ++ + struct wm8994_pdata { + int gpio_base; + +@@ -182,6 +188,16 @@ struct wm8994_pdata { + */ + int micdet_delay; + ++ /* Delay between microphone detect completing and reporting on ++ * insert (specified in ms) ++ */ ++ int mic_id_delay; ++ ++ /* Keep MICBIAS2 high for micb_en_delay, during jack insertion ++ * removal ++ */ ++ int micb_en_delay; ++ + /* IRQ for microphone detection if brought out directly as a + * signal. + */ +@@ -223,6 +239,9 @@ struct wm8994_pdata { + * lines is mastered. + */ + int max_channels_clocked[WM8994_NUM_AIF]; ++ ++ /* custom config for overriding the hw params */ ++ struct wm8958_custom_config *custom_cfg; + }; + + #endif +diff --git a/include/linux/mfd/wm8994/registers.h b/include/linux/mfd/wm8994/registers.h +index 0535489..db8cef3 100644 +--- a/include/linux/mfd/wm8994/registers.h ++++ b/include/linux/mfd/wm8994/registers.h +@@ -2668,6 +2668,10 @@ + /* + * R772 (0x304) - AIF1ADC LRCLK + */ ++#define WM8958_AIF1_LRCLK_INV 0x1000 /* AIF1_LRCLK_INV */ ++#define WM8958_AIF1_LRCLK_INV_MASK 0x1000 /* AIF1_LRCLK_INV */ ++#define WM8958_AIF1_LRCLK_INV_SHIFT 12 /* AIF1_LRCLK_INV */ ++#define WM8958_AIF1_LRCLK_INV_WIDTH 1 /* AIF1_LRCLK_INV */ + #define WM8994_AIF1ADC_LRCLK_DIR 0x0800 /* AIF1ADC_LRCLK_DIR */ + #define WM8994_AIF1ADC_LRCLK_DIR_MASK 0x0800 /* AIF1ADC_LRCLK_DIR */ + #define WM8994_AIF1ADC_LRCLK_DIR_SHIFT 11 /* AIF1ADC_LRCLK_DIR */ +@@ -2679,6 +2683,10 @@ + /* + * R773 (0x305) - AIF1DAC LRCLK + */ ++#define WM8958_AIF1_LRCLK_INV 0x1000 /* AIF1_LRCLK_INV */ ++#define WM8958_AIF1_LRCLK_INV_MASK 0x1000 /* AIF1_LRCLK_INV */ ++#define WM8958_AIF1_LRCLK_INV_SHIFT 12 /* AIF1_LRCLK_INV */ ++#define WM8958_AIF1_LRCLK_INV_WIDTH 1 /* AIF1_LRCLK_INV */ + #define WM8994_AIF1DAC_LRCLK_DIR 0x0800 /* AIF1DAC_LRCLK_DIR */ + #define WM8994_AIF1DAC_LRCLK_DIR_MASK 0x0800 /* AIF1DAC_LRCLK_DIR */ + #define WM8994_AIF1DAC_LRCLK_DIR_SHIFT 11 /* AIF1DAC_LRCLK_DIR */ diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h -index f31725b..39e8279 100644 +index f31725b..4b0fe47 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -67,6 +67,7 @@ struct mmc_ext_csd { @@ -96382,7 +100214,15 @@ index f31725b..39e8279 100644 unsigned int sectors; unsigned int card_type; unsigned int hc_erase_size; /* In sectors */ -@@ -248,6 +249,7 @@ struct mmc_card { +@@ -83,6 +84,7 @@ struct mmc_ext_csd { + unsigned int hpi_cmd; /* cmd used as HPI */ + bool bkops; /* background support bit */ + bool bkops_en; /* background enable bit */ ++ unsigned int rpmb_size; /* Units: half sector */ + unsigned int data_sector_size; /* 512 bytes or 4KB */ + unsigned int data_tag_unit_size; /* DATA TAG UNIT size */ + unsigned int boot_ro_lock; /* ro lock support */ +@@ -248,6 +250,7 @@ struct mmc_card { #define MMC_CARD_SDXC (1<<6) /* card is SDXC */ #define MMC_CARD_REMOVED (1<<7) /* card has been removed */ #define MMC_STATE_HIGHSPEED_200 (1<<8) /* card is in HS200 mode */ @@ -96390,7 +100230,7 @@ index f31725b..39e8279 100644 #define MMC_STATE_DOING_BKOPS (1<<10) /* card is doing BKOPS */ unsigned int quirks; /* card quirks */ #define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */ -@@ -264,6 +266,7 @@ struct mmc_card { +@@ -264,6 +267,7 @@ struct mmc_card { #define MMC_QUIRK_LONG_READ_TIME (1<<9) /* Data read time > CSD says */ #define MMC_QUIRK_SEC_ERASE_TRIM_BROKEN (1<<10) /* Skip secure for erase/trim */ /* byte mode */ @@ -96398,7 +100238,16 @@ index f31725b..39e8279 100644 unsigned int erase_size; /* erase size in sectors */ unsigned int erase_shift; /* if erase unit is power 2 */ -@@ -409,6 +412,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) +@@ -294,6 +298,8 @@ struct mmc_card { + struct dentry *debugfs_root; + struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */ + unsigned int nr_parts; ++ ++ unsigned int rpmb_max_req; + }; + + /* +@@ -409,6 +415,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY) #define mmc_card_highspeed(c) ((c)->state & MMC_STATE_HIGHSPEED) #define mmc_card_hs200(c) ((c)->state & MMC_STATE_HIGHSPEED_200) @@ -96406,7 +100255,7 @@ index f31725b..39e8279 100644 #define mmc_card_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR) #define mmc_card_ddr_mode(c) ((c)->state & MMC_STATE_HIGHSPEED_DDR) #define mmc_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED) -@@ -421,6 +425,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) +@@ -421,6 +428,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY) #define mmc_card_set_highspeed(c) ((c)->state |= MMC_STATE_HIGHSPEED) #define mmc_card_set_hs200(c) ((c)->state |= MMC_STATE_HIGHSPEED_200) @@ -96414,11 +100263,28 @@ index f31725b..39e8279 100644 #define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR) #define mmc_card_set_ddr_mode(c) ((c)->state |= MMC_STATE_HIGHSPEED_DDR) #define mmc_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED) +@@ -520,4 +528,7 @@ extern void mmc_unregister_driver(struct mmc_driver *); + extern void mmc_fixup_device(struct mmc_card *card, + const struct mmc_fixup *table); + ++extern int mmc_rpmb_req_handle(struct device *emmc, ++ struct mmc_ioc_rpmb_req *req); ++ + #endif /* LINUX_MMC_CARD_H */ diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h -index 39613b9..3e3b95e 100644 +index 39613b9..a4ab8e1 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h -@@ -121,6 +121,7 @@ struct mmc_data { +@@ -10,6 +10,8 @@ + + #include + #include ++#include ++#include + + struct request; + struct mmc_data; +@@ -121,6 +123,7 @@ struct mmc_data { unsigned int sg_len; /* size of scatter list */ struct scatterlist *sg; /* I/O scatter list */ s32 host_cookie; /* host private data */ @@ -96426,6 +100292,52 @@ index 39613b9..3e3b95e 100644 }; struct mmc_host; +@@ -135,6 +138,34 @@ struct mmc_request { + struct mmc_host *host; + }; + ++/* ++ * RPMB frame structure for MMC core stack ++ */ ++struct mmc_core_rpmb_req { ++ struct mmc_ioc_rpmb_req *req; ++ __u8 *frame; ++ bool ready; ++}; ++ ++#define RPMB_PROGRAM_KEY 1 /* Program RPMB Authentication Key */ ++#define RPMB_GET_WRITE_COUNTER 2 /* Read RPMB write counter */ ++#define RPMB_WRITE_DATA 3 /* Write data to RPMB partition */ ++#define RPMB_READ_DATA 4 /* Read data from RPMB partition */ ++#define RPMB_RESULT_READ 5 /* Read result request */ ++#define RPMB_REQ 1 /* RPMB request mark */ ++#define RPMB_RESP (1 << 1)/* RPMB response mark */ ++#define RPMB_AVALIABLE_SECTORS 8 /* 4K page size */ ++ ++#define RPMB_TYPE_BEG 510 ++#define RPMB_RES_BEG 508 ++#define RPMB_BLKS_BEG 506 ++#define RPMB_ADDR_BEG 504 ++#define RPMB_WCOUNTER_BEG 500 ++ ++#define RPMB_NONCE_BEG 484 ++#define RPMB_DATA_BEG 228 ++#define RPMB_MAC_BEG 196 ++ + struct mmc_card; + struct mmc_async_req; + +@@ -152,6 +183,10 @@ extern void mmc_start_bkops(struct mmc_card *card, bool from_exception); + extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, bool); + extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int); + extern int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd); ++extern int mmc_rpmb_partition_ops(struct mmc_core_rpmb_req *, ++ struct mmc_card *); ++extern int mmc_rpmb_pre_frame(struct mmc_core_rpmb_req *, struct mmc_card *); ++extern void mmc_rpmb_post_frame(struct mmc_core_rpmb_req *); + + #define MMC_ERASE_ARG 0x00000000 + #define MMC_SECURE_ERASE_ARG 0x80000000 diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index e326ae2..1416210 100644 --- a/include/linux/mmc/host.h @@ -96749,7 +100661,7 @@ index 0000000..c0580e5 + +#endif /* _LINUX_PANIC_GBUFFER_H */ diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h -index cb45b70..eacb93b 100644 +index cb45b70..6d11952 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2551,6 +2551,19 @@ @@ -96772,6 +100684,14 @@ index cb45b70..eacb93b 100644 #define PCI_DEVICE_ID_INTEL_I960RM 0x0962 #define PCI_DEVICE_ID_INTEL_CENTERTON_ILB 0x0c60 #define PCI_DEVICE_ID_INTEL_8257X_SOL 0x1062 +@@ -2862,6 +2875,7 @@ + #define PCI_DEVICE_ID_INTEL_IXP4XX 0x8500 + #define PCI_DEVICE_ID_INTEL_IXP2800 0x9004 + #define PCI_DEVICE_ID_INTEL_S21152BB 0xb152 ++#define PCI_DEVICE_ID_INTEL_SST_MRFLD 0x119A + + #define PCI_VENDOR_ID_SCALEMP 0x8686 + #define PCI_DEVICE_ID_SCALEMP_VSMP_CTL 0x1010 diff --git a/include/linux/platform_data/intel_mid_remoteproc.h b/include/linux/platform_data/intel_mid_remoteproc.h new file mode 100644 index 0000000..a16dffb @@ -97586,7 +101506,7 @@ index fe81791..cd50488 100644 u16 addr; diff --git a/include/linux/spi/intel_mid_ssp_spi.h b/include/linux/spi/intel_mid_ssp_spi.h new file mode 100644 -index 0000000..453c40b +index 0000000..2eed919 --- /dev/null +++ b/include/linux/spi/intel_mid_ssp_spi.h @@ -0,0 +1,352 @@ @@ -97701,6 +101621,7 @@ index 0000000..453c40b +DEFINE_SSP_REG(SSDR, 0x10) +DEFINE_SSP_REG(SSTO, 0x28) +DEFINE_SSP_REG(SSPSP, 0x2c) ++DEFINE_SSP_REG(SSCR2, 0x40) +DEFINE_SSP_REG(SSFS, 0x44) +DEFINE_SSP_REG(SFIFOL, 0x68) + @@ -97800,6 +101721,8 @@ index 0000000..453c40b +#define SSPSP_SFRMP (1 << 2) /* Serial Frame Polarity */ +#define SSPSP_SCMODE(x) ((x) << 0) /* Serial Bit Rate Clock Mode */ + ++#define SSCR2_CLK_DEL_EN (1 << 3) /* Delay logic for capturing input data */ ++ +/* + * For testing SSCR1 changes that require SSP restart, basically + * everything except the service and interrupt enables @@ -97812,11 +101735,6 @@ index 0000000..453c40b + | SSCR1_RFT | SSCR1_TFT | SSCR1_MWDS \ + | SSCR1_SPH | SSCR1_SPO | SSCR1_LBM) + -+/* add CS control call back feature to give user capability -+to control CS signal by themselves*/ -+#define CS_DEASSERT 0 -+#define CS_ASSERT 1 -+ +struct callback_param { + void *drv_context; + u32 direction; @@ -97893,6 +101811,8 @@ index 0000000..453c40b + unsigned long quirks; + u32 rx_fifo_threshold; + ++ /* if CS_ACTIVE_HIGH, cs_assert == 1 else cs_assert == 0 */ ++ int cs_assert; + int cs_change; + void (*cs_control)(u32 command); +}; @@ -99312,6 +103232,456 @@ index 0000000..98bbea0 +}; + +#endif +diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h +index 7cb6d36..b9e7142 100644 +--- a/include/net/bluetooth/hci_core.h ++++ b/include/net/bluetooth/hci_core.h +@@ -308,6 +308,8 @@ struct hci_conn { + + bdaddr_t dst; + __u8 dst_type; ++ bdaddr_t src; ++ __u8 src_type; + __u16 handle; + __u16 state; + __u8 mode; +diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h +index fb94cf1..bfca6cf 100644 +--- a/include/net/bluetooth/l2cap.h ++++ b/include/net/bluetooth/l2cap.h +@@ -442,7 +442,13 @@ struct l2cap_chan { + + __u8 state; + ++ bdaddr_t dst; ++ __u8 dst_type; ++ bdaddr_t src; ++ __u8 src_type; ++ + __le16 psm; ++ __le16 sport; + __u16 dcid; + __u16 scid; + +@@ -453,8 +459,6 @@ struct l2cap_chan { + __u8 chan_type; + __u8 chan_policy; + +- __le16 sport; +- + __u8 sec_level; + + __u8 ident; +diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h +index 9031a26..9307359 100644 +--- a/include/sound/compress_driver.h ++++ b/include/sound/compress_driver.h +@@ -32,6 +32,7 @@ + #include + + struct snd_compr_ops; ++struct snd_pcm_substream; + + /** + * struct snd_compr_runtime: runtime stream description +@@ -48,6 +49,8 @@ struct snd_compr_ops; + * the ring buffer + * @total_bytes_transferred: cumulative bytes transferred by offload DSP + * @sleep: poll sleep ++ * @wait: drain wait queue ++ * @drain_wake: condition for drain wake + */ + struct snd_compr_runtime { + snd_pcm_state_t state; +@@ -59,6 +62,9 @@ struct snd_compr_runtime { + u64 total_bytes_available; + u64 total_bytes_transferred; + wait_queue_head_t sleep; ++ wait_queue_head_t wait; ++ unsigned int drain_wake; ++ struct snd_pcm_substream *fe_substream; + void *private_data; + }; + +@@ -157,6 +163,7 @@ int snd_compress_register(struct snd_compr *device); + int snd_compress_deregister(struct snd_compr *device); + int snd_compress_new(struct snd_card *card, int device, + int type, struct snd_compr *compr); ++int snd_compr_stop(struct snd_compr_stream *stream); + + /* dsp driver callback apis + * For playback: driver should call snd_compress_fragment_elapsed() to let the +@@ -171,4 +178,12 @@ static inline void snd_compr_fragment_elapsed(struct snd_compr_stream *stream) + wake_up(&stream->runtime->sleep); + } + ++static inline void snd_compr_drain_notify(struct snd_compr_stream *stream) ++{ ++ snd_BUG_ON(!stream); ++ ++ stream->runtime->drain_wake = 1; ++ wake_up(&stream->runtime->wait); ++} ++ + #endif +diff --git a/include/sound/core.h b/include/sound/core.h +index 5bfe513..971a2ec 100644 +--- a/include/sound/core.h ++++ b/include/sound/core.h +@@ -146,6 +146,11 @@ struct snd_card { + struct snd_mixer_oss *mixer_oss; + int mixer_oss_change_count; + #endif ++ ++#if IS_ENABLED(CONFIG_SND_EFFECTS_OFFLOAD) ++ struct snd_effect_ops *effect_ops; ++ struct mutex effect_lock; /* effect lock */ ++#endif + }; + + #ifdef CONFIG_PM +diff --git a/include/sound/effect_driver.h b/include/sound/effect_driver.h +new file mode 100644 +index 0000000..410e5f9 +--- /dev/null ++++ b/include/sound/effect_driver.h +@@ -0,0 +1,63 @@ ++/* ++ * effect_driver.h - effect offload driver APIs ++ * ++ * Copyright (C) 2013 Intel Corporation ++ * Authors: Vinod Koul ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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 __EFFECT_DRIVER_H ++#define __EFFECT_DRIVER_H ++ ++#include ++ ++struct snd_effect_ops { ++ int (*create)(struct snd_card *card, struct snd_effect *effect); ++ int (*destroy)(struct snd_card *card, struct snd_effect *effect); ++ int (*set_params)(struct snd_card *card, ++ struct snd_effect_params *params); ++ int (*get_params)(struct snd_card *card, ++ struct snd_effect_params *params); ++ int (*query_num_effects)(struct snd_card *card); ++ int (*query_effect_caps)(struct snd_card *card, ++ struct snd_effect_caps *caps); ++}; ++ ++#if IS_ENABLED(CONFIG_SND_EFFECTS_OFFLOAD) ++int snd_effect_register(struct snd_card *card, struct snd_effect_ops *ops); ++int snd_effect_deregister(struct snd_card *card); ++#else ++static inline int snd_effect_register(struct snd_card *card, ++ struct snd_effect_ops *ops) ++{ ++ return -ENODEV; ++} ++static inline int snd_effect_deregister(struct snd_card *card) ++{ ++ return -ENODEV; ++} ++#endif ++ ++/* IOCTL fns */ ++int snd_ctl_effect_create(struct snd_card *card, void *arg); ++int snd_ctl_effect_destroy(struct snd_card *card, void *arg); ++int snd_ctl_effect_set_params(struct snd_card *card, void *arg); ++int snd_ctl_effect_get_params(struct snd_card *card, void *arg); ++int snd_ctl_effect_query_num_effects(struct snd_card *card, void *arg); ++int snd_ctl_effect_query_effect_caps(struct snd_card *card, void *arg); ++#endif +diff --git a/include/sound/intel_sst_ioctl.h b/include/sound/intel_sst_ioctl.h +new file mode 100644 +index 0000000..6025ef9 +--- /dev/null ++++ b/include/sound/intel_sst_ioctl.h +@@ -0,0 +1,63 @@ ++#ifndef __INTEL_SST_IOCTL_H__ ++#define __INTEL_SST_IOCTL_H__ ++/* ++ * intel_sst_ioctl.h - Intel SST Driver for audio engine ++ * ++ * Copyright (C) 2008-10 Intel Corporation ++ * Authors: Vinod Koul ++ * Harsha Priya ++ * Dharageswari R ++ * KP Jeeja ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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. ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * This file defines all sst ioctls ++ */ ++ ++/* codec and post/pre processing related info */ ++ ++#include ++ ++/* Pre and post processing params structure */ ++struct snd_ppp_params { ++ __u8 algo_id;/* Post/Pre processing algorithm ID */ ++ __u8 str_id; /*Only 5 bits used 0 - 31 are valid*/ ++ __u8 enable; /* 0= disable, 1= enable*/ ++ __u8 operation; /* 0 = set_algo, 1 = get_algo */ ++ __u32 size; /*Size of parameters for all blocks*/ ++ void *params; ++} __packed; ++ ++struct snd_sst_driver_info { ++ __u32 max_streams; ++}; ++ ++struct snd_sst_tuning_params { ++ __u8 type; ++ __u8 str_id; ++ __u8 size; ++ __u8 rsvd; ++ __u64 addr; ++} __packed; ++ ++/*IOCTL defined here */ ++/*SST common ioctls */ ++#define SNDRV_SST_DRIVER_INFO _IOR('L', 0x10, struct snd_sst_driver_info) ++#define SNDRV_SST_SET_ALGO _IOW('L', 0x30, struct snd_ppp_params) ++#define SNDRV_SST_GET_ALGO _IOWR('L', 0x31, struct snd_ppp_params) ++#define SNDRV_SST_TUNING_PARAMS _IOW('L', 0x32, struct snd_sst_tuning_params) ++#endif /* __INTEL_SST_IOCTL_H__ */ +diff --git a/include/sound/pcm.h b/include/sound/pcm.h +index b48792f..6f01136 100644 +--- a/include/sound/pcm.h ++++ b/include/sound/pcm.h +@@ -285,6 +285,7 @@ struct snd_pcm_runtime { + unsigned long hw_ptr_jiffies; /* Time when hw_ptr is updated */ + unsigned long hw_ptr_buffer_jiffies; /* buffer time in jiffies */ + snd_pcm_sframes_t delay; /* extra delay; typically FIFO size */ ++ snd_pcm_sframes_t soc_delay; /* extra delay; typically delay incurred in soc */ + u64 hw_ptr_wrap; /* offset for hw_ptr due to boundary wrap-around */ + + /* -- HW params -- */ +diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h +index ae9a227..41e9df9 100644 +--- a/include/sound/soc-dai.h ++++ b/include/sound/soc-dai.h +@@ -231,8 +231,8 @@ struct snd_soc_dai { + struct snd_soc_dai_driver *driver; + + /* DAI runtime info */ +- unsigned int capture_active:1; /* stream is in use */ +- unsigned int playback_active:1; /* stream is in use */ ++ unsigned int capture_active; /* cap streams is in use */ ++ unsigned int playback_active; /* pb streams is in use */ + unsigned int symmetric_rates:1; + struct snd_pcm_runtime *runtime; + unsigned int active; +diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h +index 385c632..f48d25c 100644 +--- a/include/sound/soc-dapm.h ++++ b/include/sound/soc-dapm.h +@@ -422,6 +422,8 @@ void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm); + int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, + struct snd_soc_dapm_widget_list **list); + ++struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol); ++ + /* dapm widget types */ + enum snd_soc_dapm_type { + snd_soc_dapm_input = 0, /* input pin */ +diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h +index 04598f1..a2e15ca 100644 +--- a/include/sound/soc-dpcm.h ++++ b/include/sound/soc-dpcm.h +@@ -11,6 +11,7 @@ + #ifndef __LINUX_SND_SOC_DPCM_H + #define __LINUX_SND_SOC_DPCM_H + ++#include + #include + #include + +@@ -135,4 +136,25 @@ int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute); + int soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd); + int soc_dpcm_runtime_update(struct snd_soc_dapm_widget *); + ++int dpcm_path_get(struct snd_soc_pcm_runtime *fe, ++ int stream, struct snd_soc_dapm_widget_list **list_); ++int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, ++ int stream, struct snd_soc_dapm_widget_list **list, int new); ++int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream); ++int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream); ++void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream); ++void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream); ++int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream); ++int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int tream); ++int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, int cmd); ++int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream); ++int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, ++ int event); ++ ++static inline void dpcm_path_put(struct snd_soc_dapm_widget_list **list) ++{ ++ kfree(*list); ++} ++ ++ + #endif +diff --git a/include/sound/soc.h b/include/sound/soc.h +index 85c1522..9338da8 100644 +--- a/include/sound/soc.h ++++ b/include/sound/soc.h +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + + /* + * Convenience kcontrol builders +@@ -92,6 +93,12 @@ + {.reg = xreg, .rreg = xreg, .shift = xshift, \ + .rshift = xshift, .min = xmin, .max = xmax, \ + .platform_max = xmax, .invert = xinvert} } ++#define SND_SOC_BYTES_EXT(xname, xcount, xhandler_get, xhandler_put) \ ++{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ ++ .info = snd_soc_info_bytes_ext, \ ++ .get = xhandler_get, .put = xhandler_put, \ ++ .private_value = (unsigned long)&(struct soc_bytes_ext) \ ++ {.max = xcount} } + #define SOC_DOUBLE(xname, reg, shift_left, shift_right, max, invert) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ + .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \ +@@ -420,6 +427,21 @@ struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card, + const char *dai_link, int stream); + struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card, + const char *dai_link); ++#if IS_ENABLED(CONFIG_SND_EFFECTS_OFFLOAD) ++int snd_soc_register_effect(struct snd_soc_card *card, ++ struct snd_effect_ops *ops); ++int snd_soc_unregister_effect(struct snd_soc_card *card); ++#else ++static inline int snd_soc_register_effect(struct snd_soc_card *card, ++ struct snd_effect_ops *ops) ++{ ++ return -ENODEV; ++} ++static inline int snd_soc_unregister_effect(struct snd_soc_card *card) ++{ ++ return -ENODEV; ++} ++#endif + + /* Utility functions to get clock rates from various things */ + int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots); +@@ -538,6 +560,8 @@ int snd_soc_get_strobe(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + int snd_soc_put_strobe(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); ++int snd_soc_info_bytes_ext(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *ucontrol); + + /** + * struct snd_soc_reg_access - Describes whether a given register is +@@ -594,6 +618,7 @@ struct snd_soc_jack_zone { + * @name: gpio name + * @report: value to report when jack detected + * @invert: report presence in low state ++ * @irq_flag: Interrupt flags for GPIO-Irq line + * @debouce_time: debouce time in ms + * @wake: enable as wake source + * @jack_status_check: callback function which overrides the detection +@@ -608,6 +633,7 @@ struct snd_soc_jack_gpio { + int invert; + int debounce_time; + bool wake; ++ unsigned long irq_flags; + + struct snd_soc_jack *jack; + struct delayed_work work; +@@ -932,6 +958,10 @@ struct snd_soc_dai_link { + /* machine stream operations */ + const struct snd_soc_ops *ops; + const struct snd_soc_compr_ops *compr_ops; ++ ++ /*no of substreams */ ++ unsigned int playback_count; ++ unsigned int capture_count; + }; + + struct snd_soc_codec_conf { +@@ -1063,6 +1093,7 @@ struct snd_soc_pcm_runtime { + + /* Dynamic PCM BE runtime data */ + struct snd_soc_dpcm_runtime dpcm[2]; ++ int fe_compr; + + long pmdown_time; + unsigned char pop_wait:1; +@@ -1100,6 +1131,10 @@ struct soc_mreg_control { + unsigned int regbase, regcount, nbits, invert; + }; + ++struct soc_bytes_ext { ++ int max; ++}; ++ + /* enumerated kcontrol */ + struct soc_enum { + unsigned short reg; +diff --git a/include/uapi/linux/mmc/ioctl.h b/include/uapi/linux/mmc/ioctl.h +index 1f5e689..98ad956 100644 +--- a/include/uapi/linux/mmc/ioctl.h ++++ b/include/uapi/linux/mmc/ioctl.h +@@ -47,6 +47,19 @@ struct mmc_ioc_cmd { + + #define MMC_IOC_CMD _IOWR(MMC_BLOCK_MAJOR, 0, struct mmc_ioc_cmd) + ++struct mmc_ioc_rpmb_req { ++ __u16 type; /* RPMB request type */ ++ __u16 *result; /* response or request result */ ++ __u16 blk_cnt; /* Number of blocks(half sector 256B) */ ++ __u16 addr; /* data address */ ++ __u32 *wc; /* write counter */ ++ __u8 *nonce; /* Ramdom number */ ++ __u8 *data; /* Buffer of the user data */ ++ __u8 *mac; /* Message Authentication Code */ ++}; ++ ++#define MMC_IOC_RPMB_REQ _IOWR(MMC_BLOCK_MAJOR, 1, struct mmc_ioc_rpmb_req) ++ + /* + * Since this ioctl is only meant to enhance (and not replace) normal access + * to the mmc bus device, an upper data transfer limit of MMC_IOC_MAX_BYTES diff --git a/include/uapi/linux/usb/ch9.h b/include/uapi/linux/usb/ch9.h index aa33fd1..29860d6 100644 --- a/include/uapi/linux/usb/ch9.h @@ -99324,6 +103694,107 @@ index aa33fd1..29860d6 100644 #define USB_CLASS_WIRELESS_CONTROLLER 0xe0 #define USB_CLASS_MISC 0xef #define USB_CLASS_APP_SPEC 0xfe +diff --git a/include/uapi/sound/compress_offload.h b/include/uapi/sound/compress_offload.h +index d630163..21eed48 100644 +--- a/include/uapi/sound/compress_offload.h ++++ b/include/uapi/sound/compress_offload.h +@@ -30,7 +30,7 @@ + #include + + +-#define SNDRV_COMPRESS_VERSION SNDRV_PROTOCOL_VERSION(0, 1, 1) ++#define SNDRV_COMPRESS_VERSION SNDRV_PROTOCOL_VERSION(0, 1, 2) + /** + * struct snd_compressed_buffer: compressed buffer + * @fragment_size: size of buffer fragment in bytes +@@ -67,8 +67,8 @@ struct snd_compr_params { + struct snd_compr_tstamp { + __u32 byte_offset; + __u32 copied_total; +- snd_pcm_uframes_t pcm_frames; +- snd_pcm_uframes_t pcm_io_frames; ++ __u32 pcm_frames; ++ __u32 pcm_io_frames; + __u32 sampling_rate; + }; + +@@ -80,7 +80,7 @@ struct snd_compr_tstamp { + struct snd_compr_avail { + __u64 avail; + struct snd_compr_tstamp tstamp; +-}; ++} __attribute__((packed)); + + enum snd_compr_direction { + SND_COMPRESS_PLAYBACK = 0, +diff --git a/include/uapi/sound/effect_offload.h b/include/uapi/sound/effect_offload.h +new file mode 100644 +index 0000000..9c30031 +--- /dev/null ++++ b/include/uapi/sound/effect_offload.h +@@ -0,0 +1,62 @@ ++/* ++ * effect_offload.h - effect offload header definations ++ * ++ * Copyright (C) 2013 Intel Corporation ++ * Authors: Vinod Koul ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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 __EFFECT_OFFLOAD_H ++#define __EFFECT_OFFLOAD_H ++ ++#include ++ ++#define SNDRV_EFFECT_VERSION SNDRV_PROTOCOL_VERSION(0, 1, 0) ++ ++struct snd_effect { ++ char uuid[16]; /* effect UUID */ ++ int device; /* streaming interface for effect insertion */ ++ int pos; /* position of effect to be placed in effect chain */ ++ int mode; /* Backend for Global device (Headset/Speaker) */ ++}; ++ ++struct snd_effect_params { ++ char uuid[16]; ++ int device; ++ u32 size; /* size of parameter blob */ ++ char *buffer; ++}; ++ ++struct snd_effect_caps { ++ u32 size; /* size of buffer to read effect descriptors */ ++ char *buffer; ++}; ++ ++#define SNDRV_CTL_IOCTL_EFFECT_VERSION _IOR('E', 0x00, int) ++#define SNDRV_CTL_IOCTL_EFFECT_CREATE _IOW('E', 0x01,\ ++ struct snd_effect *) ++#define SNDRV_CTL_IOCTL_EFFECT_DESTROY _IOW('E', 0x02,\ ++ struct snd_effect *) ++#define SNDRV_CTL_IOCTL_EFFECT_SET_PARAMS _IOW('E', 0x03,\ ++ struct snd_effect_params *) ++#define SNDRV_CTL_IOCTL_EFFECT_GET_PARAMS _IOWR('E', 0x04,\ ++ struct snd_effect_params *) ++#define SNDRV_CTL_IOCTL_EFFECT_QUERY_NUM _IOR('E', 0x05, int) ++#define SNDRV_CTL_IOCTL_EFFECT_QUERY_CAPS _IOWR('E', 0x06,\ ++ struct snd_effect_caps *) ++#endif diff --git a/init/main.c b/init/main.c index fc071a6..beddd35 100644 --- a/init/main.c @@ -99495,6 +103966,706 @@ index 0582a01..5546ae9 100644 } *ppos += read; +diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c +index 68843a2..1d6f790 100644 +--- a/net/bluetooth/l2cap_core.c ++++ b/net/bluetooth/l2cap_core.c +@@ -58,6 +58,18 @@ static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err); + static void l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control, + struct sk_buff_head *skbs, u8 event); + ++static inline __u8 bdaddr_type(struct hci_conn *hcon, __u8 type) ++{ ++ if (hcon->type == LE_LINK) { ++ if (type == ADDR_LE_DEV_PUBLIC) ++ return BDADDR_LE_PUBLIC; ++ else ++ return BDADDR_LE_RANDOM; ++ } ++ ++ return BDADDR_BREDR; ++} ++ + /* ---- L2CAP channels ---- */ + + static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, +@@ -1364,6 +1376,8 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) + + bacpy(&bt_sk(sk)->src, conn->src); + bacpy(&bt_sk(sk)->dst, conn->dst); ++ chan->src_type = bdaddr_type(conn->hcon, conn->hcon->src_type); ++ chan->dst_type = bdaddr_type(conn->hcon, conn->hcon->dst_type); + + l2cap_chan_add(conn, chan); + +@@ -1785,6 +1799,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, + /* Set destination address and psm */ + lock_sock(sk); + bacpy(&bt_sk(sk)->dst, dst); ++ chan->dst_type = dst_type; + release_sock(sk); + + chan->psm = psm; +@@ -1792,7 +1807,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, + + auth_type = l2cap_get_auth_type(chan); + +- if (chan->dcid == L2CAP_CID_LE_DATA) ++ if (bdaddr_type_is_le(dst_type)) + hcon = hci_connect(hdev, LE_LINK, dst, dst_type, + chan->sec_level, auth_type); + else +@@ -1825,6 +1840,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, + + /* Update source addr of the socket */ + bacpy(src, conn->src); ++ chan->src_type = bdaddr_type(hcon, hcon->src_type); + + l2cap_chan_unlock(chan); + l2cap_chan_add(conn, chan); +@@ -3755,6 +3771,8 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn, + + bacpy(&bt_sk(sk)->src, conn->src); + bacpy(&bt_sk(sk)->dst, conn->dst); ++ chan->src_type = bdaddr_type(conn->hcon, conn->hcon->src_type); ++ chan->dst_type = bdaddr_type(conn->hcon, conn->hcon->dst_type); + chan->psm = psm; + chan->dcid = scid; + chan->local_amp_id = amp_id; +diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c +index 36fed40..d0ff076 100644 +--- a/net/bluetooth/l2cap_sock.c ++++ b/net/bluetooth/l2cap_sock.c +@@ -103,7 +103,8 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) + __le16_to_cpu(la.l2_psm) == L2CAP_PSM_RFCOMM) + chan->sec_level = BT_SECURITY_SDP; + +- bacpy(&bt_sk(sk)->src, &la.l2_bdaddr); ++ bacpy(&chan->src, &la.l2_bdaddr); ++ chan->src_type = la.l2_bdaddr_type; + + chan->state = BT_BOUND; + sk->sk_state = BT_BOUND; +@@ -267,10 +268,12 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, + la->l2_psm = chan->psm; + bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst); + la->l2_cid = cpu_to_le16(chan->dcid); ++ la->l2_bdaddr_type = chan->dst_type; + } else { + la->l2_psm = chan->sport; + bacpy(&la->l2_bdaddr, &bt_sk(sk)->src); + la->l2_cid = cpu_to_le16(chan->scid); ++ la->l2_bdaddr_type = chan->src_type; + } + + return 0; +diff --git a/sound/core/Kconfig b/sound/core/Kconfig +index b413ed0..27a27bd 100644 +--- a/sound/core/Kconfig ++++ b/sound/core/Kconfig +@@ -15,6 +15,9 @@ config SND_RAWMIDI + config SND_COMPRESS_OFFLOAD + tristate + ++config SND_EFFECTS_OFFLOAD ++ tristate ++ + # To be effective this also requires INPUT - users should say: + # select SND_JACK if INPUT=y || INPUT=SND + # to avoid having to force INPUT on. +diff --git a/sound/core/Makefile b/sound/core/Makefile +index 43d4117..9e37a2b 100644 +--- a/sound/core/Makefile ++++ b/sound/core/Makefile +@@ -23,6 +23,7 @@ snd-rtctimer-objs := rtctimer.o + snd-hwdep-objs := hwdep.o + + snd-compress-objs := compress_offload.o ++snd-effects-objs := effects_offload.o + + obj-$(CONFIG_SND) += snd.o + obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o +@@ -36,3 +37,4 @@ obj-$(CONFIG_SND_OSSEMUL) += oss/ + obj-$(CONFIG_SND_SEQUENCER) += seq/ + + obj-$(CONFIG_SND_COMPRESS_OFFLOAD) += snd-compress.o ++obj-$(CONFIG_SND_EFFECTS_OFFLOAD) += snd-effects.o +diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c +index 5863ba6..632dcd6 100644 +--- a/sound/core/compress_offload.c ++++ b/sound/core/compress_offload.c +@@ -123,6 +123,7 @@ static int snd_compr_open(struct inode *inode, struct file *f) + } + runtime->state = SNDRV_PCM_STATE_OPEN; + init_waitqueue_head(&runtime->sleep); ++ init_waitqueue_head(&runtime->wait); + data->stream.runtime = runtime; + f->private_data = (void *)data; + mutex_lock(&compr->lock); +@@ -139,6 +140,18 @@ static int snd_compr_open(struct inode *inode, struct file *f) + static int snd_compr_free(struct inode *inode, struct file *f) + { + struct snd_compr_file *data = f->private_data; ++ struct snd_compr_runtime *runtime = data->stream.runtime; ++ ++ switch (runtime->state) { ++ case SNDRV_PCM_STATE_RUNNING: ++ case SNDRV_PCM_STATE_DRAINING: ++ case SNDRV_PCM_STATE_PAUSED: ++ data->stream.ops->trigger(&data->stream, SNDRV_PCM_TRIGGER_STOP); ++ break; ++ default: ++ break; ++ } ++ + data->stream.ops->free(&data->stream); + kfree(data->stream.runtime->buffer); + kfree(data->stream.runtime); +@@ -256,16 +269,25 @@ static ssize_t snd_compr_write(struct file *f, const char __user *buf, + struct snd_compr_file *data = f->private_data; + struct snd_compr_stream *stream; + size_t avail; +- int retval; ++ int retval = 0; + + if (snd_BUG_ON(!data)) + return -EFAULT; + + stream = &data->stream; + mutex_lock(&stream->device->lock); +- /* write is allowed when stream is running or has been steup */ ++ /* ++ * if the stream is in paused state, return the ++ * number of bytes consumed as 0 ++ */ ++ if (stream->runtime->state == SNDRV_PCM_STATE_PAUSED) { ++ mutex_unlock(&stream->device->lock); ++ return retval; ++ } ++ /* write is allowed when stream is running or prepared or in setup */ + if (stream->runtime->state != SNDRV_PCM_STATE_SETUP && +- stream->runtime->state != SNDRV_PCM_STATE_RUNNING) { ++ stream->runtime->state != SNDRV_PCM_STATE_RUNNING && ++ stream->runtime->state != SNDRV_PCM_STATE_PREPARED) { + mutex_unlock(&stream->device->lock); + return -EBADFD; + } +@@ -372,8 +394,7 @@ static unsigned int snd_compr_poll(struct file *f, poll_table *wait) + return -EFAULT; + + mutex_lock(&stream->device->lock); +- if (stream->runtime->state == SNDRV_PCM_STATE_PAUSED || +- stream->runtime->state == SNDRV_PCM_STATE_OPEN) { ++ if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) { + retval = -EBADFD; + goto out; + } +@@ -397,6 +418,7 @@ static unsigned int snd_compr_poll(struct file *f, poll_table *wait) + retval = snd_compr_get_poll(stream); + break; + default: ++ pr_err("poll returns err!...\n"); + if (stream->direction == SND_COMPRESS_PLAYBACK) + retval = POLLOUT | POLLWRNORM | POLLERR; + else +@@ -627,7 +649,8 @@ static int snd_compr_pause(struct snd_compr_stream *stream) + { + int retval; + +- if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) ++ if ((stream->runtime->state != SNDRV_PCM_STATE_RUNNING) && ++ (stream->runtime->state != SNDRV_PCM_STATE_DRAINING)) + return -EPERM; + retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH); + if (!retval) +@@ -642,8 +665,10 @@ static int snd_compr_resume(struct snd_compr_stream *stream) + if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED) + return -EPERM; + retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE); +- if (!retval) ++ if (!retval) { + stream->runtime->state = SNDRV_PCM_STATE_RUNNING; ++ wake_up(&stream->runtime->sleep); ++ } + return retval; + } + +@@ -659,35 +684,71 @@ static int snd_compr_start(struct snd_compr_stream *stream) + return retval; + } + +-static int snd_compr_stop(struct snd_compr_stream *stream) ++int snd_compr_stop(struct snd_compr_stream *stream) + { +- int retval; ++ int retval = 0; + +- if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || +- stream->runtime->state == SNDRV_PCM_STATE_SETUP) ++ if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) + return -EPERM; +- retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP); ++ if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED) ++ retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP); + if (!retval) { + stream->runtime->state = SNDRV_PCM_STATE_SETUP; + wake_up(&stream->runtime->sleep); ++ snd_compr_drain_notify(stream); + stream->runtime->total_bytes_available = 0; + stream->runtime->total_bytes_transferred = 0; + } + return retval; + } ++EXPORT_SYMBOL(snd_compr_stop); ++ ++static int snd_compress_wait_for_drain(struct snd_compr_stream *stream) ++{ ++ /* ++ * We are called with lock held. So drop the lock while we wait for ++ * drain complete notfication from the driver ++ * ++ * It is expected that driver will notify the drain completion and then ++ * stream will be moved to SETUP state, even if draining resulted in an ++ * error. We can trigger next track after this. ++ */ ++ stream->runtime->state = SNDRV_PCM_STATE_DRAINING; ++ mutex_unlock(&stream->device->lock); ++ ++ wait_event(stream->runtime->wait, stream->runtime->drain_wake); ++ ++ wake_up(&stream->runtime->sleep); ++ mutex_lock(&stream->device->lock); ++ ++ return 0; ++} + + static int snd_compr_drain(struct snd_compr_stream *stream) + { +- int retval; ++ int retval = 0; + +- if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || +- stream->runtime->state == SNDRV_PCM_STATE_SETUP) ++ if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) + return -EPERM; +- retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN); +- if (!retval) { +- stream->runtime->state = SNDRV_PCM_STATE_DRAINING; ++ ++ stream->runtime->drain_wake = 0; ++ ++ /* this is hackish for our tree but for now lets carry it while we fix ++ * usermode behaviour ++ */ ++ if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED) ++ retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN); ++ else ++ return 0; ++ ++ if (retval) { ++ pr_err("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval); + wake_up(&stream->runtime->sleep); ++ return retval; + } ++ ++ retval = snd_compress_wait_for_drain(stream); ++ stream->runtime->state = SNDRV_PCM_STATE_SETUP; + return retval; + } + +@@ -715,17 +776,30 @@ static int snd_compr_next_track(struct snd_compr_stream *stream) + + static int snd_compr_partial_drain(struct snd_compr_stream *stream) + { +- int retval; +- if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || +- stream->runtime->state == SNDRV_PCM_STATE_SETUP) ++ int retval = 0; ++ ++ /* agaain hackish changes */ ++ if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) + return -EPERM; + /* stream can be drained only when next track has been signalled */ + if (stream->next_track == false) + return -EPERM; + +- retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN); ++ stream->runtime->drain_wake = 0; ++ if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED) ++ retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN); ++ else ++ return 0; ++ ++ if (retval) { ++ pr_err("Partial drain returned failure\n"); ++ wake_up(&stream->runtime->sleep); ++ return retval; ++ } + + stream->next_track = false; ++ retval = snd_compress_wait_for_drain(stream); ++ stream->runtime->state = SNDRV_PCM_STATE_SETUP; + return retval; + } + +@@ -804,6 +878,9 @@ static const struct file_operations snd_compr_file_ops = { + .write = snd_compr_write, + .read = snd_compr_read, + .unlocked_ioctl = snd_compr_ioctl, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl = snd_compr_ioctl, ++#endif + .mmap = snd_compr_mmap, + .poll = snd_compr_poll, + }; +@@ -818,7 +895,7 @@ static int snd_compress_dev_register(struct snd_device *device) + return -EBADFD; + compr = device->device_data; + +- sprintf(str, "comprC%iD%i", compr->card->number, compr->device); ++ snprintf(str, sizeof(str), "comprC%iD%i", compr->card->number, compr->device); + pr_debug("reg %s for device %s, direction %d\n", str, compr->name, + compr->direction); + /* register compressed device */ +diff --git a/sound/core/effects_offload.c b/sound/core/effects_offload.c +new file mode 100644 +index 0000000..def0a89 +--- /dev/null ++++ b/sound/core/effects_offload.c +@@ -0,0 +1,307 @@ ++/* ++ * effect_offload.c - effects offload core ++ * ++ * Copyright (C) 2013 Intel Corporation ++ * Authors: Lakshmi N Vinnakota ++ * Vinod Koul ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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 FORMAT(fmt) "%s: %d: " fmt, __func__, __LINE__ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt) ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static DEFINE_MUTEX(effect_mutex); ++ ++int snd_ctl_effect_create(struct snd_card *card, void *arg) ++{ ++ int retval = 0; ++ struct snd_effect *effect; ++ ++ effect = kmalloc(sizeof(*effect), GFP_KERNEL); ++ if (!effect) ++ return -ENOMEM; ++ if (copy_from_user(effect, (void __user *)arg, sizeof(*effect))) { ++ retval = -EFAULT; ++ goto out; ++ } ++ pr_debug("effect_offload: device %d, pos %d, mode%d\n", ++ effect->device, effect->pos, effect->mode); ++ ++ mutex_lock(&card->effect_lock); ++ retval = card->effect_ops->create(card, effect); ++ mutex_unlock(&card->effect_lock); ++out: ++ kfree(effect); ++ return retval; ++} ++EXPORT_SYMBOL_GPL(snd_ctl_effect_create); ++ ++int snd_ctl_effect_destroy(struct snd_card *card, void *arg) ++{ ++ int retval = 0; ++ struct snd_effect *effect; ++ ++ effect = kmalloc(sizeof(*effect), GFP_KERNEL); ++ if (!effect) ++ return -ENOMEM; ++ if (copy_from_user(effect, (void __user *)arg, sizeof(*effect))) { ++ retval = -EFAULT; ++ goto out; ++ } ++ mutex_lock(&card->effect_lock); ++ retval = card->effect_ops->destroy(card, effect); ++ mutex_unlock(&card->effect_lock); ++out: ++ kfree(effect); ++ return retval; ++} ++EXPORT_SYMBOL_GPL(snd_ctl_effect_destroy); ++ ++int snd_ctl_effect_set_params(struct snd_card *card, void *arg) ++{ ++ int retval = 0; ++ struct snd_effect_params *params; ++ char __user *argp = (char __user *)arg; ++ char __user *bufp; ++ ++ params = kmalloc(sizeof(*params), GFP_KERNEL); ++ if (!params) ++ return -ENOMEM; ++ ++ if (copy_from_user(params, argp, sizeof(*params))) { ++ retval = -EFAULT; ++ goto out; ++ } ++ bufp = params->buffer; ++ params->buffer = kmalloc(params->size, GFP_KERNEL); ++ if (!params->buffer) { ++ retval = -ENOMEM; ++ goto out; ++ } ++ ++ if (copy_from_user((void *)params->buffer, bufp, params->size)) { ++ retval = -EFAULT; ++ goto free_buf; ++ } ++ ++ mutex_lock(&card->effect_lock); ++ retval = card->effect_ops->set_params(card, params); ++ mutex_unlock(&card->effect_lock); ++free_buf: ++ kfree(params->buffer); ++out: ++ kfree(params); ++ return retval; ++} ++EXPORT_SYMBOL_GPL(snd_ctl_effect_set_params); ++ ++int snd_ctl_effect_get_params(struct snd_card *card, void *arg) ++{ ++ int retval = 0; ++ struct snd_effect_params inparams; ++ struct snd_effect_params *outparams; ++ unsigned int offset; ++ char __user *argp = (char __user *)arg; ++ ++ if (copy_from_user((void *)&inparams, argp, sizeof(inparams))) ++ retval = -EFAULT; ++ ++ outparams = kmalloc(sizeof(*outparams), GFP_KERNEL); ++ if (!outparams) ++ return -ENOMEM; ++ ++ memcpy(outparams, &inparams, sizeof(inparams)); ++ outparams->buffer = kmalloc(inparams.size, GFP_KERNEL); ++ if (!outparams->buffer) { ++ retval = -ENOMEM; ++ goto free_out; ++ } ++ ++ if (copy_from_user((void *)outparams->buffer, inparams.buffer, ++ inparams.size)) { ++ retval = -EFAULT; ++ goto free_buf; ++ } ++ ++ mutex_lock(&card->effect_lock); ++ retval = card->effect_ops->get_params(card, outparams); ++ mutex_unlock(&card->effect_lock); ++ ++ if (retval) ++ goto free_buf; ++ ++ if (!outparams->size) ++ goto free_buf; ++ ++ if (outparams->size > inparams.size) { ++ pr_err("mem insufficient to copy\n"); ++ retval = -EMSGSIZE; ++ goto free_buf; ++ } else { ++ offset = offsetof(struct snd_effect_params, size); ++ if (copy_to_user((argp + offset), (void *)&outparams->size, ++ sizeof(u32))) ++ retval = -EFAULT; ++ ++ if (copy_to_user(inparams.buffer, outparams->buffer, ++ outparams->size)) ++ retval = -EFAULT; ++ } ++free_buf: ++ kfree(outparams->buffer); ++free_out: ++ kfree(outparams); ++ return retval; ++} ++EXPORT_SYMBOL_GPL(snd_ctl_effect_get_params); ++ ++int snd_ctl_effect_query_num_effects(struct snd_card *card, void *arg) ++{ ++ int retval = 0; ++ int __user *ip = arg; ++ ++ mutex_lock(&card->effect_lock); ++ retval = card->effect_ops->query_num_effects(card); ++ mutex_unlock(&card->effect_lock); ++ ++ if (retval < 0) ++ goto out; ++ retval = put_user(retval, ip) ? -EFAULT : 0; ++out: ++ return retval; ++} ++EXPORT_SYMBOL_GPL(snd_ctl_effect_query_num_effects); ++ ++int snd_ctl_effect_query_effect_caps(struct snd_card *card, void *arg) ++{ ++ int retval = 0; ++ struct snd_effect_caps *caps; ++ unsigned int offset, insize; ++ char __user *argp = (char __user *)arg; ++ char __user *bufp; ++ ++ caps = kmalloc(sizeof(*caps), GFP_KERNEL); ++ if (!caps) ++ return -ENOMEM; ++ ++ if (copy_from_user(caps, argp, sizeof(*caps))) { ++ retval = -EFAULT; ++ goto out; ++ } ++ bufp = caps->buffer; ++ insize = caps->size; ++ caps->buffer = kmalloc(caps->size, GFP_KERNEL); ++ if (!caps->buffer) { ++ retval = -ENOMEM; ++ goto out; ++ } ++ ++ mutex_lock(&card->effect_lock); ++ retval = card->effect_ops->query_effect_caps(card, caps); ++ mutex_unlock(&card->effect_lock); ++ ++ if (retval) ++ goto free_buf; ++ ++ if (insize < caps->size) { ++ pr_err("mem insufficient to copy\n"); ++ retval = -EMSGSIZE; ++ goto free_buf; ++ } ++ ++ offset = offsetof(struct snd_effect_caps, size); ++ if (copy_to_user((argp + offset), (void *)&caps->size, sizeof(u32))) { ++ retval = -EFAULT; ++ goto free_buf; ++ } ++ ++ if (copy_to_user(bufp, caps->buffer, caps->size)) ++ retval = -EFAULT; ++ ++free_buf: ++ kfree(caps->buffer); ++out: ++ kfree(caps); ++ return retval; ++} ++EXPORT_SYMBOL_GPL(snd_ctl_effect_query_effect_caps); ++ ++/** ++ * snd_effect_register - register compressed device ++ * ++ * @card : snd card to which the effect is registered ++ * @ops : effect_ops to register ++ */ ++int snd_effect_register(struct snd_card *card, struct snd_effect_ops *ops) ++{ ++ ++ if (card == NULL || ops == NULL) ++ return -EINVAL; ++ ++ if (snd_BUG_ON(!ops->create)) ++ return -EINVAL; ++ if (snd_BUG_ON(!ops->destroy)) ++ return -EINVAL; ++ if (snd_BUG_ON(!ops->set_params)) ++ return -EINVAL; ++ ++ mutex_init(&card->effect_lock); ++ ++ pr_debug("Registering Effects to card %s\n", card->shortname); ++ /* register the effect ops with the card */ ++ mutex_lock(&effect_mutex); ++ card->effect_ops = ops; ++ mutex_unlock(&effect_mutex); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(snd_effect_register); ++ ++int snd_effect_deregister(struct snd_card *card) ++{ ++ pr_debug("Removing effects for card %s\n", card->shortname); ++ mutex_lock(&effect_mutex); ++ card->effect_ops = NULL; ++ mutex_unlock(&effect_mutex); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(snd_effect_deregister); ++ ++static int __init snd_effect_init(void) ++{ ++ return 0; ++} ++ ++static void __exit snd_effect_exit(void) ++{ ++} ++ ++module_init(snd_effect_init); ++module_exit(snd_effect_exit); ++ ++MODULE_DESCRIPTION("ALSA Effect offload framework"); ++MODULE_AUTHOR("Lakshmi N Vinnakota "); ++MODULE_AUTHOR("Vinod Koul "); ++MODULE_LICENSE("GPL v2"); +diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig +index 9e675c7..23f39d2 100644 +--- a/sound/soc/Kconfig ++++ b/sound/soc/Kconfig +@@ -45,7 +45,7 @@ source "sound/soc/jz4740/Kconfig" + source "sound/soc/nuc900/Kconfig" + source "sound/soc/omap/Kconfig" + source "sound/soc/kirkwood/Kconfig" +-source "sound/soc/mid-x86/Kconfig" ++source "sound/soc/intel/Kconfig" + source "sound/soc/mxs/Kconfig" + source "sound/soc/pxa/Kconfig" + source "sound/soc/samsung/Kconfig" +diff --git a/sound/soc/Makefile b/sound/soc/Makefile +index 197b6ae..25201f53 100644 +--- a/sound/soc/Makefile ++++ b/sound/soc/Makefile +@@ -20,7 +20,7 @@ obj-$(CONFIG_SND_SOC) += davinci/ + obj-$(CONFIG_SND_SOC) += dwc/ + obj-$(CONFIG_SND_SOC) += fsl/ + obj-$(CONFIG_SND_SOC) += jz4740/ +-obj-$(CONFIG_SND_SOC) += mid-x86/ ++obj-$(CONFIG_SND_SOC) += intel/ + obj-$(CONFIG_SND_SOC) += mxs/ + obj-$(CONFIG_SND_SOC) += nuc900/ + obj-$(CONFIG_SND_SOC) += omap/ diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c index d1ae869d..fc12d6e 100644 --- a/sound/soc/codecs/sn95031.c @@ -99508,6 +104679,25160 @@ index d1ae869d..fc12d6e 100644 #include #include #include +diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c +index 29e95f9..97b7b8f 100644 +--- a/sound/soc/codecs/wm8994.c ++++ b/sound/soc/codecs/wm8994.c +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -93,9 +94,9 @@ static int wm8994_retune_mobile_base[] = { + + static const struct wm8958_micd_rate micdet_rates[] = { + { 32768, true, 1, 4 }, +- { 32768, false, 1, 1 }, ++ { 32768, false, 1, 0 }, + { 44100 * 256, true, 7, 10 }, +- { 44100 * 256, false, 7, 10 }, ++ { 44100 * 256, false, 7, 9 }, + }; + + static const struct wm8958_micd_rate jackdet_rates[] = { +@@ -539,13 +540,13 @@ static const struct soc_enum adc_osr = + static const struct snd_kcontrol_new wm8994_snd_controls[] = { + SOC_DOUBLE_R_TLV("AIF1ADC1 Volume", WM8994_AIF1_ADC1_LEFT_VOLUME, + WM8994_AIF1_ADC1_RIGHT_VOLUME, +- 1, 119, 0, digital_tlv), ++ 1, 120, 0, digital_tlv), + SOC_DOUBLE_R_TLV("AIF1ADC2 Volume", WM8994_AIF1_ADC2_LEFT_VOLUME, + WM8994_AIF1_ADC2_RIGHT_VOLUME, +- 1, 119, 0, digital_tlv), ++ 1, 120, 0, digital_tlv), + SOC_DOUBLE_R_TLV("AIF2ADC Volume", WM8994_AIF2_ADC_LEFT_VOLUME, + WM8994_AIF2_ADC_RIGHT_VOLUME, +- 1, 119, 0, digital_tlv), ++ 1, 120, 0, digital_tlv), + + SOC_ENUM("AIF1ADCL Source", aif1adcl_src), + SOC_ENUM("AIF1ADCR Source", aif1adcr_src), +@@ -607,12 +608,12 @@ SOC_ENUM("ADC OSR", adc_osr), + SOC_ENUM("DAC OSR", dac_osr), + + SOC_DOUBLE_R_TLV("DAC1 Volume", WM8994_DAC1_LEFT_VOLUME, +- WM8994_DAC1_RIGHT_VOLUME, 1, 96, 0, digital_tlv), ++ WM8994_DAC1_RIGHT_VOLUME, 1, 112, 0, digital_tlv), + SOC_DOUBLE_R("DAC1 Switch", WM8994_DAC1_LEFT_VOLUME, + WM8994_DAC1_RIGHT_VOLUME, 9, 1, 1), + + SOC_DOUBLE_R_TLV("DAC2 Volume", WM8994_DAC2_LEFT_VOLUME, +- WM8994_DAC2_RIGHT_VOLUME, 1, 96, 0, digital_tlv), ++ WM8994_DAC2_RIGHT_VOLUME, 1, 112, 0, digital_tlv), + SOC_DOUBLE_R("DAC2 Switch", WM8994_DAC2_LEFT_VOLUME, + WM8994_DAC2_RIGHT_VOLUME, 9, 1, 1), + +@@ -638,6 +639,11 @@ SOC_SINGLE_TLV("AIF2DAC 3D Stereo Volume", WM8994_AIF2_DAC_FILTERS_2, + 10, 15, 0, wm8994_3d_tlv), + SOC_SINGLE("AIF2DAC 3D Stereo Switch", WM8994_AIF2_DAC_FILTERS_2, + 8, 1, 0), ++ ++SOC_SINGLE_TLV("MIXINL MIXOUTL Volume", WM8994_INPUT_MIXER_3, 0, 7, 0, ++ mixin_boost_tlv), ++SOC_SINGLE_TLV("MIXINR MIXOUTR Volume", WM8994_INPUT_MIXER_4, 0, 7, 0, ++ mixin_boost_tlv), + }; + + static const struct snd_kcontrol_new wm8994_eq_controls[] = { +@@ -866,7 +872,7 @@ static void vmid_reference(struct snd_soc_codec *codec) + WM8994_BIAS_SRC | + WM8994_STARTUP_BIAS_ENA | + WM8994_VMID_BUF_ENA | +- (0x2 << WM8994_VMID_RAMP_SHIFT)); ++ (0x3 << WM8994_VMID_RAMP_SHIFT)); + + /* Main bias enable, VMID=2x40k */ + snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1, +@@ -874,7 +880,14 @@ static void vmid_reference(struct snd_soc_codec *codec) + WM8994_VMID_SEL_MASK, + WM8994_BIAS_ENA | 0x2); + +- msleep(300); ++ /* The delay of 300ms was recommended to support pop ++ * free startup of the line output driver, as we don't use ++ * that feature reducing the delay to 50ms as recommended in ++ * the spec, Also changing VMID_RAMP to soft fast start ++ * accordingly Also applies for VMID_FORCE and ++ * vmid_dereference. ++ */ ++ msleep(50); + + snd_soc_update_bits(codec, WM8994_ANTIPOP_2, + WM8994_VMID_RAMP_MASK | +@@ -893,15 +906,14 @@ static void vmid_reference(struct snd_soc_codec *codec) + WM8994_BIAS_SRC | + WM8994_STARTUP_BIAS_ENA | + WM8994_VMID_BUF_ENA | +- (0x2 << WM8994_VMID_RAMP_SHIFT)); ++ (0x3 << WM8994_VMID_RAMP_SHIFT)); + + /* Main bias enable, VMID=2x40k */ + snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1, + WM8994_BIAS_ENA | + WM8994_VMID_SEL_MASK, + WM8994_BIAS_ENA | 0x2); +- +- msleep(400); ++ msleep(50); + + snd_soc_update_bits(codec, WM8994_ANTIPOP_2, + WM8994_VMID_RAMP_MASK | +@@ -946,7 +958,7 @@ static void vmid_dereference(struct snd_soc_codec *codec) + snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1, + WM8994_VMID_SEL_MASK, 0); + +- msleep(400); ++ msleep(50); + + /* Active discharge */ + snd_soc_update_bits(codec, WM8994_ANTIPOP_1, +@@ -969,7 +981,7 @@ static void vmid_dereference(struct snd_soc_codec *codec) + WM8994_VMID_RAMP_MASK, 0); + + snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1, +- WM8994_VMID_SEL_MASK, 0); ++ WM8994_BIAS_ENA | WM8994_VMID_SEL_MASK, 0); + } + + pm_runtime_put(codec->dev); +@@ -1055,7 +1067,7 @@ static int aif1clk_ev(struct snd_soc_dapm_widget *w, + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Don't enable timeslot 2 if not in use */ +- if (wm8994->channels[0] <= 2) ++ if ((wm8994->channels[0] <= 2) && (wm8994->slots <= 2)) + mask &= ~(WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA); + + val = snd_soc_read(codec, WM8994_AIF1_CONTROL_1); +@@ -1331,6 +1343,26 @@ static int dac_ev(struct snd_soc_dapm_widget *w, + struct snd_soc_codec *codec = w->codec; + unsigned int mask = 1 << w->shift; + ++ /* Don't propagate FIFO errors unless the DAC is running */ ++ switch (event) { ++ case SND_SOC_DAPM_POST_PMU: ++ /* Clear FIFO error status */ ++ snd_soc_update_bits(codec, WM8994_INTERRUPT_STATUS_2, ++ WM8994_FIFOS_ERR_EINT_MASK, ++ 1 << WM8994_FIFOS_ERR_EINT_SHIFT); ++ /* Unmask FIFO error interrupts */ ++ snd_soc_update_bits(codec, WM8994_INTERRUPT_STATUS_2_MASK, ++ WM8994_IM_FIFOS_ERR_EINT_MASK, ++ 0 << WM8994_IM_FIFOS_ERR_EINT_SHIFT); ++ break; ++ case SND_SOC_DAPM_PRE_PMD: ++ /* Mask FIFO error interrupts */ ++ snd_soc_update_bits(codec, WM8994_INTERRUPT_STATUS_2_MASK, ++ WM8994_IM_FIFOS_ERR_EINT_MASK, ++ 1 << WM8994_IM_FIFOS_ERR_EINT_SHIFT); ++ break; ++ } ++ + snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5, + mask, mask); + return 0; +@@ -1498,6 +1530,24 @@ static const char *aif1dac_text[] = { + "AIF1DACDAT", "AIF3DACDAT", + }; + ++static const char *loopback_text[] = { ++ "None", "ADCDAT", ++}; ++ ++static const struct soc_enum aif1_loopback_enum = ++ SOC_ENUM_SINGLE(WM8994_AIF1_CONTROL_2, WM8994_AIF1_LOOPBACK_SHIFT, 2, ++ loopback_text); ++ ++static const struct snd_kcontrol_new aif1_loopback = ++ SOC_DAPM_ENUM("AIF1 Loopback", aif1_loopback_enum); ++ ++static const struct soc_enum aif2_loopback_enum = ++ SOC_ENUM_SINGLE(WM8994_AIF2_CONTROL_2, WM8994_AIF2_LOOPBACK_SHIFT, 2, ++ loopback_text); ++ ++static const struct snd_kcontrol_new aif2_loopback = ++ SOC_DAPM_ENUM("AIF2 Loopback", aif2_loopback_enum); ++ + static const struct soc_enum aif1dac_enum = + SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 0, 2, aif1dac_text); + +@@ -1616,13 +1666,17 @@ SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpr_mux), + + static const struct snd_soc_dapm_widget wm8994_dac_revd_widgets[] = { + SND_SOC_DAPM_DAC_E("DAC2L", NULL, SND_SOC_NOPM, 3, 0, +- dac_ev, SND_SOC_DAPM_PRE_PMU), ++ dac_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | ++ SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_DAC_E("DAC2R", NULL, SND_SOC_NOPM, 2, 0, +- dac_ev, SND_SOC_DAPM_PRE_PMU), ++ dac_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | ++ SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_DAC_E("DAC1L", NULL, SND_SOC_NOPM, 1, 0, +- dac_ev, SND_SOC_DAPM_PRE_PMU), ++ dac_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | ++ SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_DAC_E("DAC1R", NULL, SND_SOC_NOPM, 0, 0, +- dac_ev, SND_SOC_DAPM_PRE_PMU), ++ dac_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | ++ SND_SOC_DAPM_PRE_PMD), + }; + + static const struct snd_soc_dapm_widget wm8994_dac_widgets[] = { +@@ -1633,15 +1687,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_VIRT_MUX_E("ADCL Mux", SND_SOC_NOPM, 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_VIRT_MUX_E("ADCR Mux", SND_SOC_NOPM, 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_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), + }; + + static const struct snd_soc_dapm_widget wm8994_dapm_widgets[] = { +@@ -1737,12 +1791,11 @@ SND_SOC_DAPM_ADC("DMIC2R", NULL, WM8994_POWER_MANAGEMENT_4, 4, 0), + SND_SOC_DAPM_ADC("DMIC1L", NULL, WM8994_POWER_MANAGEMENT_4, 3, 0), + SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8994_POWER_MANAGEMENT_4, 2, 0), + +-/* Power is done with the muxes since the ADC power also controls the +- * downsampling chain, the chip will automatically manage the analogue +- * specific portions. +- */ +-SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 1, 0), +-SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0), ++SND_SOC_DAPM_ADC("ADCL", NULL, WM8994_POWER_MANAGEMENT_4, 1, 0), ++SND_SOC_DAPM_ADC("ADCR", NULL, WM8994_POWER_MANAGEMENT_4, 0, 0), ++ ++SND_SOC_DAPM_MUX("AIF1 Loopback", SND_SOC_NOPM, 0, 0, &aif1_loopback), ++SND_SOC_DAPM_MUX("AIF2 Loopback", SND_SOC_NOPM, 0, 0, &aif2_loopback), + + SND_SOC_DAPM_POST("Debug log", post_ev), + }; +@@ -1875,9 +1928,9 @@ static const struct snd_soc_dapm_route intercon[] = { + { "AIF1DAC2L", NULL, "AIF1DAC Mux" }, + { "AIF1DAC2R", NULL, "AIF1DAC Mux" }, + +- { "AIF1DAC Mux", "AIF1DACDAT", "AIF1DACDAT" }, ++ { "AIF1DAC Mux", "AIF1DACDAT", "AIF1 Loopback" }, + { "AIF1DAC Mux", "AIF3DACDAT", "AIF3DACDAT" }, +- { "AIF2DAC Mux", "AIF2DACDAT", "AIF2DACDAT" }, ++ { "AIF2DAC Mux", "AIF2DACDAT", "AIF2 Loopback" }, + { "AIF2DAC Mux", "AIF3DACDAT", "AIF3DACDAT" }, + { "AIF2ADC Mux", "AIF2ADCDAT", "AIF2ADCL" }, + { "AIF2ADC Mux", "AIF2ADCDAT", "AIF2ADCR" }, +@@ -1928,6 +1981,12 @@ static const struct snd_soc_dapm_route intercon[] = { + { "AIF3ADCDAT", "AIF2DACDAT", "AIF2DACL" }, + { "AIF3ADCDAT", "AIF2DACDAT", "AIF2DACR" }, + ++ /* Loopback */ ++ { "AIF1 Loopback", "ADCDAT", "AIF1ADCDAT" }, ++ { "AIF1 Loopback", "None", "AIF1DACDAT" }, ++ { "AIF2 Loopback", "ADCDAT", "AIF2ADCDAT" }, ++ { "AIF2 Loopback", "None", "AIF2DACDAT" }, ++ + /* Sidetone */ + { "Left Sidetone", "ADC/DMIC1", "ADCL Mux" }, + { "Left Sidetone", "DMIC2", "DMIC2L" }, +@@ -2010,15 +2069,16 @@ struct fll_div { + u16 outdiv; + u16 n; + u16 k; ++ u16 lambda; + u16 clk_ref_div; + u16 fll_fratio; + }; + +-static int wm8994_get_fll_config(struct fll_div *fll, ++static int wm8994_get_fll_config(struct wm8994 *control, struct fll_div *fll, + int freq_in, int freq_out) + { + u64 Kpart; +- unsigned int K, Ndiv, Nmod; ++ unsigned int K, Ndiv, Nmod, gcd_fll; + + pr_debug("FLL input=%dHz, output=%dHz\n", freq_in, freq_out); + +@@ -2067,20 +2127,32 @@ static int wm8994_get_fll_config(struct fll_div *fll, + Nmod = freq_out % freq_in; + pr_debug("Nmod=%d\n", Nmod); + +- /* Calculate fractional part - scale up so we can round. */ +- Kpart = FIXED_FLL_SIZE * (long long)Nmod; ++ switch (control->type) { ++ case WM8994: ++ /* Calculate fractional part - scale up so we can round. */ ++ Kpart = FIXED_FLL_SIZE * (long long)Nmod; ++ ++ do_div(Kpart, freq_in); ++ ++ K = Kpart & 0xFFFFFFFF; + +- do_div(Kpart, freq_in); ++ if ((K % 10) >= 5) ++ K += 5; + +- K = Kpart & 0xFFFFFFFF; ++ /* Move down to proper range now rounding is done */ ++ fll->k = K / 10; ++ fll->lambda = 0; + +- if ((K % 10) >= 5) +- K += 5; ++ pr_debug("N=%x K=%x\n", fll->n, fll->k); ++ break; + +- /* Move down to proper range now rounding is done */ +- fll->k = K / 10; ++ default: ++ gcd_fll = gcd(freq_out, freq_in); + +- pr_debug("N=%x K=%x\n", fll->n, fll->k); ++ fll->k = (freq_out - (freq_in * fll->n)) / gcd_fll; ++ fll->lambda = freq_in / gcd_fll; ++ ++ } + + return 0; + } +@@ -2144,9 +2216,9 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src, + * analysis bugs spewing warnings. + */ + if (freq_out) +- ret = wm8994_get_fll_config(&fll, freq_in, freq_out); ++ ret = wm8994_get_fll_config(control, &fll, freq_in, freq_out); + else +- ret = wm8994_get_fll_config(&fll, wm8994->fll[id].in, ++ ret = wm8994_get_fll_config(control, &fll, wm8994->fll[id].in, + wm8994->fll[id].out); + if (ret < 0) + return ret; +@@ -2191,6 +2263,17 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src, + WM8994_FLL1_N_MASK, + fll.n << WM8994_FLL1_N_SHIFT); + ++ if (fll.lambda) { ++ snd_soc_update_bits(codec, WM8958_FLL1_EFS_1 + reg_offset, ++ WM8958_FLL1_LAMBDA_MASK, ++ fll.lambda); ++ snd_soc_update_bits(codec, WM8958_FLL1_EFS_2 + reg_offset, ++ WM8958_FLL1_EFS_ENA, WM8958_FLL1_EFS_ENA); ++ } else { ++ snd_soc_update_bits(codec, WM8958_FLL1_EFS_2 + reg_offset, ++ WM8958_FLL1_EFS_ENA, 0); ++ } ++ + snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_5 + reg_offset, + WM8994_FLL1_FRC_NCO | WM8958_FLL1_BYP | + WM8994_FLL1_REFCLK_DIV_MASK | +@@ -2271,7 +2354,8 @@ out: + * If SYSCLK will be less than 50kHz adjust AIFnCLK dividers + * for detection. + */ +- if (max(wm8994->aifclk[0], wm8994->aifclk[1]) < 50000) { ++ if (max(wm8994->aifclk[0], wm8994->aifclk[1]) < 50000 && ++ !wm8994->aifdiv[0]) { + dev_dbg(codec->dev, "Configuring AIFs for 128fs\n"); + + wm8994->aifdiv[0] = snd_soc_read(codec, WM8994_AIF1_RATE) +@@ -2555,17 +2639,24 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) + struct wm8994 *control = wm8994->wm8994; + int ms_reg; + int aif1_reg; ++ int dac_reg; ++ int adc_reg; + int ms = 0; + int aif1 = 0; ++ int lrclk = 0; + + switch (dai->id) { + case 1: + ms_reg = WM8994_AIF1_MASTER_SLAVE; + aif1_reg = WM8994_AIF1_CONTROL_1; ++ dac_reg = WM8994_AIF1DAC_LRCLK; ++ adc_reg = WM8994_AIF1ADC_LRCLK; + break; + case 2: + ms_reg = WM8994_AIF2_MASTER_SLAVE; + aif1_reg = WM8994_AIF2_CONTROL_1; ++ dac_reg = WM8994_AIF1DAC_LRCLK; ++ adc_reg = WM8994_AIF1ADC_LRCLK; + break; + default: + return -EINVAL; +@@ -2584,6 +2675,7 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_B: + aif1 |= WM8994_AIF1_LRCLK_INV; ++ lrclk |= WM8958_AIF1_LRCLK_INV; + case SND_SOC_DAIFMT_DSP_A: + aif1 |= 0x18; + break; +@@ -2622,12 +2714,14 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) + break; + case SND_SOC_DAIFMT_IB_IF: + aif1 |= WM8994_AIF1_BCLK_INV | WM8994_AIF1_LRCLK_INV; ++ lrclk |= WM8958_AIF1_LRCLK_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + aif1 |= WM8994_AIF1_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + aif1 |= WM8994_AIF1_LRCLK_INV; ++ lrclk |= WM8958_AIF1_LRCLK_INV; + break; + default: + return -EINVAL; +@@ -2658,6 +2752,10 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) + aif1); + snd_soc_update_bits(codec, ms_reg, WM8994_AIF1_MSTR, + ms); ++ snd_soc_update_bits(codec, dac_reg, ++ WM8958_AIF1_LRCLK_INV, lrclk); ++ snd_soc_update_bits(codec, adc_reg, ++ WM8958_AIF1_LRCLK_INV, lrclk); + + return 0; + } +@@ -2706,9 +2804,40 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, + int lrclk = 0; + int rate_val = 0; + int id = dai->id - 1; ++ struct snd_pcm_hw_params hw_params; + + int i, cur_val, best_val, bclk_rate, best; + ++ if (params) ++ memcpy(&hw_params, params, sizeof(*params)); ++ else ++ return -EINVAL; ++ ++ /* If custom params are there, override to custom params */ ++ if (pdata->custom_cfg) { ++ ++ dev_dbg(codec->dev, "%s: Overriding to custom params....\n", ++ __func__); ++ ++ snd_mask_none(hw_param_mask(&hw_params, ++ SNDRV_PCM_HW_PARAM_FORMAT)); ++ snd_mask_set(hw_param_mask(&hw_params, ++ SNDRV_PCM_HW_PARAM_FORMAT), ++ pdata->custom_cfg->format); ++ ++ hw_param_interval(&hw_params, SNDRV_PCM_HW_PARAM_RATE)->min = ++ pdata->custom_cfg->rate; ++ hw_param_interval(&hw_params, SNDRV_PCM_HW_PARAM_RATE)->max = ++ pdata->custom_cfg->rate; ++ ++ hw_param_interval(&hw_params, ++ SNDRV_PCM_HW_PARAM_CHANNELS)->min = ++ pdata->custom_cfg->channels; ++ hw_param_interval(&hw_params, ++ SNDRV_PCM_HW_PARAM_CHANNELS)->max = ++ pdata->custom_cfg->channels; ++ } ++ + switch (dai->id) { + case 1: + aif1_reg = WM8994_AIF1_CONTROL_1; +@@ -2740,8 +2869,9 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, + return -EINVAL; + } + +- bclk_rate = params_rate(params); +- switch (params_format(params)) { ++ bclk_rate = params_rate(&hw_params); ++ ++ switch (params_format(&hw_params)) { + case SNDRV_PCM_FORMAT_S16_LE: + bclk_rate *= 16; + break; +@@ -2761,7 +2891,7 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, + return -EINVAL; + } + +- wm8994->channels[id] = params_channels(params); ++ wm8994->channels[id] = params_channels(&hw_params); + if (pdata->max_channels_clocked[id] && + wm8994->channels[id] > pdata->max_channels_clocked[id]) { + dev_dbg(dai->dev, "Constraining channels to %d from %d\n", +@@ -2781,7 +2911,7 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, + + /* Try to find an appropriate sample rate; look for an exact match. */ + for (i = 0; i < ARRAY_SIZE(srs); i++) +- if (srs[i].rate == params_rate(params)) ++ if (srs[i].rate == params_rate(&hw_params)) + break; + if (i == ARRAY_SIZE(srs)) + return -EINVAL; +@@ -2802,10 +2932,10 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, + + /* AIFCLK/fs ratio; look for a close match in either direction */ + best = 0; +- best_val = abs((fs_ratios[0] * params_rate(params)) ++ best_val = abs((fs_ratios[0] * params_rate(&hw_params)) + - wm8994->aifclk[id]); + for (i = 1; i < ARRAY_SIZE(fs_ratios); i++) { +- cur_val = abs((fs_ratios[i] * params_rate(params)) ++ cur_val = abs((fs_ratios[i] * params_rate(&hw_params)) + - wm8994->aifclk[id]); + if (cur_val >= best_val) + continue; +@@ -2833,7 +2963,7 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, + bclk_divs[best], bclk_rate); + bclk |= best << WM8994_AIF1_BCLK_DIV_SHIFT; + +- lrclk = bclk_rate / params_rate(params); ++ lrclk = bclk_rate / params_rate(&hw_params); + if (!lrclk) { + dev_err(dai->dev, "Unable to generate LRCLK from %dHz BCLK\n", + bclk_rate); +@@ -2853,12 +2983,12 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (dai->id) { + case 1: +- wm8994->dac_rates[0] = params_rate(params); ++ wm8994->dac_rates[0] = params_rate(&hw_params); + wm8994_set_retune_mobile(codec, 0); + wm8994_set_retune_mobile(codec, 1); + break; + case 2: +- wm8994->dac_rates[1] = params_rate(params); ++ wm8994->dac_rates[1] = params_rate(&hw_params); + wm8994_set_retune_mobile(codec, 2); + break; + } +@@ -2911,6 +3041,13 @@ static int wm8994_aif3_hw_params(struct snd_pcm_substream *substream, + return snd_soc_update_bits(codec, aif1_reg, WM8994_AIF1_WL_MASK, aif1); + } + ++#if IS_ENABLED(CONFIG_SND_MRFLD_MACHINE) || \ ++ IS_ENABLED(CONFIG_SND_MOOR_MACHINE) ++static int wm8994_aif_mute(struct snd_soc_dai *codec_dai, int mute) ++{ ++ return 0; ++} ++#else + static int wm8994_aif_mute(struct snd_soc_dai *codec_dai, int mute) + { + struct snd_soc_codec *codec = codec_dai->codec; +@@ -2937,6 +3074,7 @@ static int wm8994_aif_mute(struct snd_soc_dai *codec_dai, int mute) + + return 0; + } ++#endif + + static int wm8994_set_tristate(struct snd_soc_dai *codec_dai, int tristate) + { +@@ -2964,6 +3102,25 @@ static int wm8994_set_tristate(struct snd_soc_dai *codec_dai, int tristate) + return snd_soc_update_bits(codec, reg, mask, val); + } + ++static int wm8994_set_tdm_slots(struct snd_soc_dai *dai, ++ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) ++{ ++ struct snd_soc_codec *codec = dai->codec; ++ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); ++ struct wm8994 *control = wm8994->wm8994; ++ ++ switch (control->type) { ++ case WM8958: ++ wm8994->slots = slots; ++ break; ++ default: ++ pr_err("we dont support tdm for non 8958!"); ++ return -EINVAL; ++ break; ++ } ++ return 0; ++} ++ + static int wm8994_aif2_probe(struct snd_soc_dai *dai) + { + struct snd_soc_codec *codec = dai->codec; +@@ -2991,6 +3148,7 @@ static const struct snd_soc_dai_ops wm8994_aif1_dai_ops = { + .digital_mute = wm8994_aif_mute, + .set_pll = wm8994_set_fll, + .set_tristate = wm8994_set_tristate, ++ .set_tdm_slot = wm8994_set_tdm_slots, + }; + + static const struct snd_soc_dai_ops wm8994_aif2_dai_ops = { +@@ -3096,24 +3254,7 @@ static int wm8994_codec_suspend(struct snd_soc_codec *codec) + static int wm8994_codec_resume(struct snd_soc_codec *codec) + { + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); +- struct wm8994 *control = wm8994->wm8994; + int i, ret; +- unsigned int val, mask; +- +- if (control->revision < 4) { +- /* force a HW read */ +- ret = regmap_read(control->regmap, +- WM8994_POWER_MANAGEMENT_5, &val); +- +- /* modify the cache only */ +- codec->cache_only = 1; +- mask = WM8994_DAC1R_ENA | WM8994_DAC1L_ENA | +- WM8994_DAC2R_ENA | WM8994_DAC2L_ENA; +- val &= mask; +- snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5, +- mask, val); +- codec->cache_only = 0; +- } + + for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) { + if (!wm8994->fll_suspend[i].out) +@@ -3495,6 +3636,32 @@ static void wm8958_button_det(struct snd_soc_codec *codec, u16 status) + wm8994->btn_mask); + } + ++static void wm8958_open_circuit_work(struct work_struct *work) ++{ ++ struct wm8994_priv *wm8994 = container_of(work, ++ struct wm8994_priv, ++ open_circuit_work.work); ++ struct device *dev = wm8994->wm8994->dev; ++ ++ wm1811_micd_stop(wm8994->hubs.codec); ++ ++ mutex_lock(&wm8994->accdet_lock); ++ ++ dev_dbg(dev, "Reporting open circuit\n"); ++ ++ wm8994->jack_mic = false; ++ wm8994->mic_detecting = true; ++ wm8994->headphone_detected = false; ++ ++ wm8958_micd_set_rate(wm8994->hubs.codec); ++ ++ snd_soc_jack_report(wm8994->micdet[0].jack, 0, ++ wm8994->btn_mask | ++ SND_JACK_HEADSET); ++ ++ mutex_unlock(&wm8994->accdet_lock); ++} ++ + static void wm8958_mic_id(void *data, u16 status) + { + struct snd_soc_codec *codec = data; +@@ -3504,16 +3671,9 @@ static void wm8958_mic_id(void *data, u16 status) + if (!(status & WM8958_MICD_STS)) { + /* If nothing present then clear our statuses */ + dev_dbg(codec->dev, "Detected open circuit\n"); +- wm8994->jack_mic = false; +- wm8994->mic_detecting = true; +- +- wm1811_micd_stop(codec); + +- wm8958_micd_set_rate(codec); +- +- snd_soc_jack_report(wm8994->micdet[0].jack, 0, +- wm8994->btn_mask | +- SND_JACK_HEADSET); ++ schedule_delayed_work(&wm8994->open_circuit_work, ++ msecs_to_jiffies(2500)); + return; + } + +@@ -3568,10 +3728,8 @@ static void wm1811_mic_work(struct work_struct *work) + + dev_dbg(codec->dev, "Starting mic detection\n"); + +- /* Use a user-supplied callback if we have one */ +- if (wm8994->micd_cb) { +- wm8994->micd_cb(wm8994->micd_cb_data); +- } else { ++ /* If there's a callback it'll be called out of the lock */ ++ if (!wm8994->micd_cb) { + /* + * Start off measument of microphone impedence to find out + * what's actually there. +@@ -3585,6 +3743,10 @@ static void wm1811_mic_work(struct work_struct *work) + + mutex_unlock(&wm8994->accdet_lock); + ++ /* Custom callbacks may reasonably wish to take the same locks */ ++ if (wm8994->micd_cb) ++ wm8994->micd_cb(wm8994->micd_cb_data); ++ + pm_runtime_put(codec->dev); + } + +@@ -3596,8 +3758,12 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data) + int reg, delay; + bool present; + ++ cancel_delayed_work_sync(&wm8994->mic_work); ++ + pm_runtime_get_sync(codec->dev); + ++ cancel_delayed_work_sync(&wm8994->mic_complete_work); ++ + mutex_lock(&wm8994->accdet_lock); + + reg = snd_soc_read(codec, WM1811_JACKDET_CTRL); +@@ -3630,8 +3796,6 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data) + } else { + dev_dbg(codec->dev, "Jack not detected\n"); + +- cancel_delayed_work_sync(&wm8994->mic_work); +- + snd_soc_update_bits(codec, WM8958_MICBIAS2, + WM8958_MICB2_DISCH, WM8958_MICB2_DISCH); + +@@ -3720,6 +3884,7 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, + } else { + wm8994->mic_detecting = true; + wm8994->jack_mic = false; ++ wm8994->headphone_detected = false; + } + + if (id_cb) { +@@ -3780,11 +3945,70 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, + } + EXPORT_SYMBOL_GPL(wm8958_mic_detect); + ++int wm8958_micd_set_custom_rate(struct snd_soc_codec *codec, ++ wm8958_micd_set_custom_rate_cb micd_custom_rate_cb, ++ void *micd_custom_rate_cb_data) ++{ ++ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); ++ ++ if (micd_custom_rate_cb) { ++ wm8994->micd_custom_rate_cb = micd_custom_rate_cb; ++ wm8994->micd_custom_rate_cb_data = micd_custom_rate_cb_data; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(wm8958_micd_set_custom_rate); ++ ++static void wm8958_mic_work(struct work_struct *work) ++{ ++ struct wm8994_priv *wm8994 = container_of(work, ++ struct wm8994_priv, ++ mic_complete_work.work); ++ struct snd_soc_codec *codec = wm8994->hubs.codec; ++ ++ dev_crit(codec->dev, "MIC WORK %x\n", wm8994->mic_status); ++ ++ pm_runtime_get_sync(codec->dev); ++ ++ mutex_lock(&wm8994->accdet_lock); ++ ++ wm8994->mic_id_cb(wm8994->mic_id_cb_data, wm8994->mic_status); ++ ++ mutex_unlock(&wm8994->accdet_lock); ++ ++ pm_runtime_put(codec->dev); ++ ++ dev_crit(codec->dev, "MIC WORK %x DONE\n", wm8994->mic_status); ++} ++ ++static void wm8958_micd_set_custom_rate_work(struct work_struct *work) ++{ ++ struct wm8994_priv *wm8994 = container_of(work, ++ struct wm8994_priv, ++ micd_set_custom_rate_work.work); ++ struct snd_soc_codec *codec = wm8994->hubs.codec; ++ ++ dev_dbg(codec->dev, "%s: Set custom rates\n", __func__); ++ ++ pm_runtime_get_sync(codec->dev); ++ ++ mutex_lock(&wm8994->accdet_lock); ++ ++ wm8994->micd_custom_rate_cb(wm8994->micd_custom_rate_cb_data); ++ ++ mutex_unlock(&wm8994->accdet_lock); ++ ++ pm_runtime_put(codec->dev); ++ ++} ++ + static irqreturn_t wm8958_mic_irq(int irq, void *data) + { + struct wm8994_priv *wm8994 = data; + struct snd_soc_codec *codec = wm8994->hubs.codec; +- int reg, count, ret; ++ struct wm8994 *control = wm8994->wm8994; ++ int reg, count, ret, id_delay; + + /* + * Jack detection may have detected a removal simulataneously +@@ -3794,6 +4018,9 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data) + if (!(snd_soc_read(codec, WM8958_MIC_DETECT_1) & WM8958_MICD_ENA)) + return IRQ_HANDLED; + ++ cancel_delayed_work_sync(&wm8994->mic_complete_work); ++ cancel_delayed_work_sync(&wm8994->open_circuit_work); ++ + pm_runtime_get_sync(codec->dev); + + /* We may occasionally read a detection without an impedence +@@ -3842,14 +4069,30 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data) + snd_soc_jack_report(wm8994->micdet[0].jack, 0, + SND_JACK_MECHANICAL | SND_JACK_HEADSET | + wm8994->btn_mask); ++ wm8994->jack_mic = false; ++ wm8994->headphone_detected = false; + wm8994->mic_detecting = true; + goto out; + } + +- if (wm8994->mic_detecting) +- wm8994->mic_id_cb(wm8994->mic_id_cb_data, reg); +- else ++ wm8994->mic_status = reg; ++ id_delay = wm8994->wm8994->pdata.mic_id_delay; ++ ++ if (wm8994->mic_detecting) { ++ if (control->type == WM8958) { ++ /* Set mic-bias high during detection phase (micb_en_delay) */ ++ /* 0 == Continuous */ ++ dev_dbg(codec->dev, "Set MICBIAS High, for micb_en_delay time\n"); ++ snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, ++ WM8958_MICD_BIAS_STARTTIME_MASK | ++ WM8958_MICD_RATE_MASK, 0); ++ } ++ ++ schedule_delayed_work(&wm8994->mic_complete_work, ++ msecs_to_jiffies(id_delay)); ++ } else { + wm8958_button_det(codec, reg); ++ } + + out: + pm_runtime_put(codec->dev); +@@ -3888,6 +4131,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) + struct wm8994 *control = dev_get_drvdata(codec->dev->parent); + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = &codec->dapm; ++ unsigned int dcs_done_irq; + unsigned int reg; + int ret, i; + +@@ -3899,6 +4143,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) + mutex_init(&wm8994->accdet_lock); + INIT_DELAYED_WORK(&wm8994->jackdet_bootstrap, + wm1811_jackdet_bootstrap); ++ INIT_DELAYED_WORK(&wm8994->open_circuit_work, ++ wm8958_open_circuit_work); + + switch (control->type) { + case WM8994: +@@ -3907,10 +4153,16 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) + case WM1811: + INIT_DELAYED_WORK(&wm8994->mic_work, wm1811_mic_work); + break; ++ case WM8958: ++ INIT_DELAYED_WORK(&wm8994->micd_set_custom_rate_work, ++ wm8958_micd_set_custom_rate_work); ++ break; + default: + break; + } + ++ INIT_DELAYED_WORK(&wm8994->mic_complete_work, wm8958_mic_work); ++ + for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) + init_completion(&wm8994->fll_locked[i]); + +@@ -3983,6 +4235,9 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) + wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_TEMP_SHUT, + wm8994_temp_shut, "Thermal shutdown", codec); + ++ dcs_done_irq = regmap_irq_get_virq(wm8994->wm8994->irq_data, ++ WM8994_IRQ_DCS_DONE); ++ irq_set_status_flags(dcs_done_irq, IRQ_NOAUTOEN); + ret = wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_DCS_DONE, + wm_hubs_dcs_done, "DC servo done", + &wm8994->hubs); +@@ -4089,7 +4344,6 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) + } + if ((reg & WM8994_GPN_FN_MASK) != WM8994_GP_FN_PIN_SPECIFIC) { + wm8994->lrclk_shared[0] = 1; +- wm8994_dai[0].symmetric_rates = 1; + } else { + wm8994->lrclk_shared[0] = 0; + } +@@ -4219,6 +4473,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) + } + + wm_hubs_add_analogue_routes(codec, 0, 0); ++ enable_irq(dcs_done_irq); + snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); + + switch (control->type) { +@@ -4261,6 +4516,11 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) + break; + } + ++ /* Make sure FIFO errors are masked */ ++ snd_soc_update_bits(codec, WM8994_INTERRUPT_STATUS_2_MASK, ++ WM8994_IM_FIFOS_ERR_EINT_MASK, ++ 1 << WM8994_IM_FIFOS_ERR_EINT_SHIFT); ++ + return 0; + + err_irq: +@@ -4365,6 +4625,11 @@ static int wm8994_remove(struct platform_device *pdev) + static int wm8994_suspend(struct device *dev) + { + struct wm8994_priv *wm8994 = dev_get_drvdata(dev); ++ struct wm8994 *control = wm8994->wm8994; ++ struct snd_soc_codec *codec = wm8994->hubs.codec; ++ unsigned int reg; ++ int ret; ++ + + /* Drop down to power saving mode when system is suspended */ + if (wm8994->jackdet && !wm8994->active_refcount) +@@ -4372,18 +4637,62 @@ static int wm8994_suspend(struct device *dev) + WM1811_JACKDET_MODE_MASK, + wm8994->jackdet_mode); + ++ /* Disable the MIC Detection when suspended */ ++ if ((control->type == WM8958) && wm8994->mic_id_cb) { ++ ++ reg = snd_soc_read(codec, WM8958_MIC_DETECT_3); ++ ++ dev_dbg(codec->dev, "%s: WM8958_MIC_DETECT_3 0x%x\n", __func__, reg); ++ dev_dbg(codec->dev, "mic_detect %d jack_mic %d headphone %d\n", ++ wm8994->mic_detecting, wm8994->jack_mic, ++ wm8994->headphone_detected); ++ ++ if (!(wm8994->jack_mic) && !(wm8994->headphone_detected)) { ++ ++ dev_dbg(codec->dev, "Jack not connected..Mask interrupt\n"); ++ snd_soc_write(codec, WM8994_INTERRUPT_CONTROL, 0x01); ++ ++ ret = regcache_sync_region(wm8994->wm8994->regmap, ++ WM8994_INTERRUPT_CONTROL, ++ WM8994_INTERRUPT_CONTROL); ++ if (ret != 0) ++ dev_err(dev, "Failed to sync register: %d\n", ret); ++ synchronize_irq(control->irq); ++ ++ dev_dbg(codec->dev, "Disable MIC Detection!!!\n"); ++ snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, ++ WM8958_MICD_ENA, 0); ++ ++ snd_soc_dapm_disable_pin(&codec->dapm, "CLK_SYS"); ++ snd_soc_dapm_sync(&codec->dapm); ++ } ++ } ++ + return 0; + } + + static int wm8994_resume(struct device *dev) + { + struct wm8994_priv *wm8994 = dev_get_drvdata(dev); ++ struct wm8994 *control = wm8994->wm8994; ++ struct snd_soc_codec *codec = wm8994->hubs.codec; + + if (wm8994->jackdet && wm8994->jackdet_mode) + regmap_update_bits(wm8994->wm8994->regmap, WM8994_ANTIPOP_2, + WM1811_JACKDET_MODE_MASK, + WM1811_JACKDET_MODE_AUDIO); + ++ /* Enable the MIC Detection when resumed */ ++ if ((control->type == WM8958) && wm8994->mic_id_cb) { ++ dev_dbg(codec->dev, "Enable MIC Detection!!!\n"); ++ snd_soc_dapm_force_enable_pin(&codec->dapm, "CLK_SYS"); ++ snd_soc_dapm_sync(&codec->dapm); ++ ++ snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, ++ WM8958_MICD_ENA, WM8958_MICD_ENA); ++ snd_soc_write(codec, WM8994_INTERRUPT_CONTROL, 0x00); ++ } ++ + return 0; + } + #endif +diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h +index 55ddf4d..0a834df 100644 +--- a/sound/soc/codecs/wm8994.h ++++ b/sound/soc/codecs/wm8994.h +@@ -41,6 +41,7 @@ enum wm8994_vmid_mode { + + typedef void (*wm1811_micdet_cb)(void *data); + typedef void (*wm1811_mic_id_cb)(void *data, u16 status); ++typedef void (*wm8958_micd_set_custom_rate_cb)(struct snd_soc_codec *codec); + + int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, + int micbias); +@@ -55,6 +56,10 @@ int wm8958_aif_ev(struct snd_soc_dapm_widget *w, + + void wm8958_dsp2_init(struct snd_soc_codec *codec); + ++int wm8958_micd_set_custom_rate(struct snd_soc_codec *codec, ++ wm8958_micd_set_custom_rate_cb micd_custom_rate_cb, ++ void *micd_custom_rate_cb_data); ++ + struct wm8994_micdet { + struct snd_soc_jack *jack; + bool detecting; +@@ -86,6 +91,7 @@ struct wm8994_priv { + bool fll_locked_irq; + bool fll_byp; + bool clk_has_run; ++ int slots; + + int vmid_refcount; + int active_refcount; +@@ -134,8 +140,14 @@ struct wm8994_priv { + struct mutex accdet_lock; + struct wm8994_micdet micdet[2]; + struct delayed_work mic_work; ++ struct delayed_work open_circuit_work; ++ struct delayed_work mic_complete_work; ++ struct delayed_work micd_set_custom_rate_work; ++ ++ u16 mic_status; + bool mic_detecting; + bool jack_mic; ++ bool headphone_detected; + int btn_mask; + bool jackdet; + int jackdet_mode; +@@ -146,6 +158,8 @@ struct wm8994_priv { + void *micd_cb_data; + wm1811_mic_id_cb mic_id_cb; + void *mic_id_cb_data; ++ wm8958_micd_set_custom_rate_cb micd_custom_rate_cb; ++ void *micd_custom_rate_cb_data; + + unsigned int aif1clk_enable:1; + unsigned int aif2clk_enable:1; +diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c +index f5d81b9..2874ec9 100644 +--- a/sound/soc/codecs/wm_hubs.c ++++ b/sound/soc/codecs/wm_hubs.c +@@ -530,6 +530,7 @@ static int hp_supply_event(struct snd_soc_dapm_widget *w, + hubs->hp_startup_mode); + break; + } ++ break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1, +@@ -1222,11 +1223,6 @@ int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *codec, + WM8993_LINEOUT2_MODE, + WM8993_LINEOUT2_MODE); + +- if (!lineout1_diff && !lineout2_diff) +- snd_soc_update_bits(codec, WM8993_ANTIPOP1, +- WM8993_LINEOUT_VMID_BUF_ENA, +- WM8993_LINEOUT_VMID_BUF_ENA); +- + if (lineout1fb) + snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL, + WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB); +@@ -1252,6 +1248,13 @@ void wm_hubs_vmid_ena(struct snd_soc_codec *codec) + struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec); + int val = 0; + ++ if ((hubs->lineout1_se && hubs->lineout2_se) && ++ (hubs->lineout1n_ena || hubs->lineout1p_ena || ++ hubs->lineout2n_ena || hubs->lineout2p_ena)) ++ snd_soc_update_bits(codec, WM8993_ANTIPOP1, ++ WM8993_LINEOUT_VMID_BUF_ENA, ++ WM8993_LINEOUT_VMID_BUF_ENA); ++ + if (hubs->lineout1_se) + val |= WM8993_LINEOUT1N_ENA | WM8993_LINEOUT1P_ENA; + +@@ -1281,6 +1284,13 @@ void wm_hubs_set_bias_level(struct snd_soc_codec *codec, + val = 0; + mask = 0; + ++ if ((hubs->lineout1_se && hubs->lineout2_se) && ++ (hubs->lineout1n_ena || hubs->lineout1p_ena || ++ hubs->lineout2n_ena || hubs->lineout2p_ena)) ++ snd_soc_update_bits(codec, WM8993_ANTIPOP1, ++ WM8993_LINEOUT_VMID_BUF_ENA, ++ WM8993_LINEOUT_VMID_BUF_ENA); ++ + if (hubs->lineout1_se) + mask |= WM8993_LINEOUT1N_ENA | WM8993_LINEOUT1P_ENA; + +diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig +new file mode 100644 +index 0000000..b6efaac +--- /dev/null ++++ b/sound/soc/intel/Kconfig +@@ -0,0 +1,58 @@ ++config SND_MFLD_MACHINE ++ tristate "SOC Machine Audio driver for Intel Medfield MID platform" ++ depends on INTEL_SCU_IPC && INTEL_SCU_IPC_UTIL && X86 && GPIO_LANGWELL ++ depends on MSIC_GPADC ++ select SND_SOC_SN95031 ++ select SND_SST_PLATFORM ++ select SND_SST_MACHINE ++ select SND_INTEL_SST ++ default n ++ help ++ This adds support for ASoC machine driver for Intel(R) MID Medfield platform ++ used as alsa device in audio subsystem in Intel(R) MID devices ++ Say Y if you have such a device ++ If unsure select "N". ++ ++config SND_MRFLD_MACHINE ++ tristate "SOC Machine Audio driver for Intel Merrifield MID platform" ++ depends on INTEL_SCU_IPC && X86 ++ select SND_SOC_LM49453 ++ select SND_SOC_WM8994 ++ select MFD_CORE ++ select MFD_WM8994 ++ select REGULATOR_WM8994 ++ select SND_SST_PLATFORM ++ select SND_SST_MACHINE ++ select SND_INTEL_SST ++ select SND_EFFECTS_OFFLOAD ++ default n ++ help ++ This adds support for ASoC machine driver for Intel(R) MID Merrifield platform ++ used as alsa device in audio substem in Intel(R) MID devices ++ Say Y if you have such a device ++ If unsure select "N". ++ ++config SND_INTEL_SST ++ tristate ++ ++config SND_SST_PLATFORM ++ tristate ++ ++config SND_SOC_COMMS_SSP ++ depends on SND_INTEL_MID_I2S ++ tristate "Use ASOC framework to drive AudioComms SSP BT and Modem" ++ help ++ Sound SOC cards usually used for BT VOIP and MODEM MIXING use cases. ++ This will add devices for these uses cases in the list of alsa cards. ++ Say Y if you need these sound cards (BT chipset or Modem present). ++ Requires to enable the INTEL_MID_I2S low level SSP I2S driver. ++ ++config SST_MRFLD_DPCM ++ bool "Use DPCM based Merrifield Machine Audio driver" ++ default n ++ help ++ This adds an option to enable the DPCM based MRFLD machine driver ++ ++config SND_SST_MACHINE ++ tristate ++ +diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile +new file mode 100644 +index 0000000..02d5120 +--- /dev/null ++++ b/sound/soc/intel/Makefile +@@ -0,0 +1,22 @@ ++#EXTRA CFLAGS ++ccflags-y += -Werror ++ ++ifeq (${TARGET_BUILD_VARIANT},$(filter ${TARGET_BUILD_VARIANT}, eng)) ++ccflags-y += -DCONFIG_SND_VERBOSE_PRINTK -DCONFIG_SND_DEBUG -DCONFIG_SND_DEBUG_VERBOSE ++endif ++ ++# SST Platform Driver ++PLATFORM_LIBS = platform-libs/controls_v1.o platform-libs/controls_v2.o platform-libs/controls_v2_dpcm.o \ ++ platform-libs/ipc_lib_v2.o ++ ++snd-soc-sst-platform-objs := pcm.o compress.o effects.o $(PLATFORM_LIBS) ++obj-$(CONFIG_SND_SST_PLATFORM) += snd-soc-sst-platform.o ++ ++# Relevant Machine driver ++obj-$(CONFIG_SND_SST_MACHINE) += board/ ++ ++# DSP driver ++obj-$(CONFIG_SND_INTEL_SST) += sst/ ++ ++# Audio Comms ++obj-$(CONFIG_SND_SOC_COMMS_SSP) += ssp/ +diff --git a/sound/soc/intel/board/Makefile b/sound/soc/intel/board/Makefile +new file mode 100644 +index 0000000..3581ae1 +--- /dev/null ++++ b/sound/soc/intel/board/Makefile +@@ -0,0 +1,14 @@ ++#EXTRA CFLAGS ++ccflags-y += -Werror ++ ++# Merrifield board ++snd-merr-saltbay-wm8958-objs := merr_saltbay_wm8958.o ++snd-merr-dpcm-wm8958-objs := merr_dpcm_wm8958.o ++snd-merr-dpcm-dummy-objs := merr_dpcm_dummy.o ++ ++ifdef CONFIG_SST_MRFLD_DPCM ++ obj-$(CONFIG_SND_MRFLD_MACHINE) += snd-merr-dpcm-wm8958.o ++ obj-$(CONFIG_SND_MRFLD_MACHINE) += snd-merr-dpcm-dummy.o ++else ++ obj-$(CONFIG_SND_MRFLD_MACHINE) += snd-merr-saltbay-wm8958.o ++endif +diff --git a/sound/soc/intel/board/merr_dpcm_dummy.c b/sound/soc/intel/board/merr_dpcm_dummy.c +new file mode 100644 +index 0000000..3cb879c +--- /dev/null ++++ b/sound/soc/intel/board/merr_dpcm_dummy.c +@@ -0,0 +1,233 @@ ++/* ++ * ASoc Dummy DPCM Machine driver for Intel Edison MID platform ++ * ++ * Copyright (C) 2014 Intel Corp ++ * Author: Michael Soares ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static bool wm_hardware_avail = true; ++module_param(wm_hardware_avail, bool, S_IRUSR); ++MODULE_PARM_DESC(wm_hardware_avail, "1 in cmdline to use dummy version"); ++ ++#ifdef CONFIG_PM_SLEEP ++static int snd_merr_dpcm_prepare(struct device *dev) ++{ ++ pr_debug("In %s device name\n", __func__); ++ snd_soc_suspend(dev); ++ return 0; ++} ++ ++static void snd_merr_dpcm_complete(struct device *dev) ++{ ++ pr_debug("In %s\n", __func__); ++ snd_soc_resume(dev); ++ return; ++} ++ ++static int snd_merr_dpcm_poweroff(struct device *dev) ++{ ++ pr_debug("In %s\n", __func__); ++ snd_soc_poweroff(dev); ++ return 0; ++} ++#else ++#define snd_merr_dpcm_prepare NULL ++#define snd_merr_dpcm_complete NULL ++#define snd_merr_dpcm_poweroff NULL ++#endif ++ ++static unsigned int rates_48000[] = { ++ 48000, ++}; ++ ++static struct snd_pcm_hw_constraint_list constraints_48000 = { ++ .count = ARRAY_SIZE(rates_48000), ++ .list = rates_48000, ++}; ++ ++static int merr_dummy_startup(struct snd_pcm_substream *substream) ++{ ++ return snd_pcm_hw_constraint_list(substream->runtime, 0, ++ SNDRV_PCM_HW_PARAM_RATE, ++ &constraints_48000); ++} ++ ++static struct snd_soc_ops merr_dummy_ops = { ++ .startup = merr_dummy_startup, ++}; ++ ++static int merr_codec_fixup(struct snd_soc_pcm_runtime *rtd, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_interval *rate = hw_param_interval(params, ++ SNDRV_PCM_HW_PARAM_RATE); ++ struct snd_interval *channels = hw_param_interval(params, ++ SNDRV_PCM_HW_PARAM_CHANNELS); ++ ++ pr_debug("Invoked %s for dailink %s\n", __func__, rtd->dai_link->name); ++ ++ /* The DSP will convert the FE rate to 48k, stereo, 24bits */ ++ rate->min = rate->max = 48000; ++ channels->min = channels->max = 2; ++ ++ /* set SSP2 to 24-bit */ ++ snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - ++ SNDRV_PCM_HW_PARAM_FIRST_MASK], ++ SNDRV_PCM_FORMAT_S24_LE); ++ return 0; ++} ++ ++struct snd_soc_dai_link merr_msic_dailink[] = { ++ [MERR_DPCM_AUDIO] = { ++ .name = "Media Audio Port", ++ .stream_name = "Edison Audio", ++ .cpu_dai_name = "Headset-cpu-dai", ++ .codec_name = "snd-soc-dummy", ++ .codec_dai_name = "snd-soc-dummy-dai", ++ .platform_name = "sst-platform", ++ .ignore_suspend = 1, ++ .dynamic = 1, ++ .ops = &merr_dummy_ops, ++ }, ++ /* back ends */ ++ { ++ .name = "SSP2-Codec", ++ .be_id = 1, ++ .cpu_dai_name = "ssp2-codec", ++ .platform_name = "sst-platform", ++ .no_pcm = 1, ++ .codec_dai_name = "snd-soc-dummy-dai", ++ .codec_name = "snd-soc-dummy", ++ .be_hw_params_fixup = merr_codec_fixup, ++ .ignore_suspend = 1, ++ }, ++ { ++ .name = "SSP1-BT", ++ .be_id = 2, ++ .cpu_dai_name = "snd-soc-dummy-dai", ++ .platform_name = "snd-soc-dummy", ++ .no_pcm = 1, ++ .codec_name = "snd-soc-dummy", ++ .codec_dai_name = "snd-soc-dummy-dai", ++ .ignore_suspend = 1, ++ }, ++ ++}; ++ ++static const struct snd_soc_dapm_route map[] = { ++ { "Dummy Playback", NULL, "codec_out0" }, ++ { "Dummy Playback", NULL, "codec_out1" }, ++ { "codec_in0", NULL, "Dummy Capture" }, ++ { "codec_in1", NULL, "Dummy Capture" }, ++}; ++ ++/* SoC card */ ++static struct snd_soc_card snd_soc_card_merr = { ++ .name = "dummy-audio", ++ .dai_link = merr_msic_dailink, ++ .num_links = ARRAY_SIZE(merr_msic_dailink), ++ .dapm_routes = map, ++ .num_dapm_routes = ARRAY_SIZE(map), ++}; ++ ++static int snd_merr_dpcm_probe(struct platform_device *pdev) ++{ ++ int ret_val = 0; ++ pr_debug("%s enter\n", __func__); ++ ++ /* register the soc card */ ++ snd_soc_card_merr.dev = &pdev->dev; ++ ret_val = snd_soc_register_card(&snd_soc_card_merr); ++ if (ret_val) { ++ pr_err("snd_soc_register_card failed %d\n", ret_val); ++ return ret_val; ++ } ++ platform_set_drvdata(pdev, &snd_soc_card_merr); ++ pr_info("%s successful\n", __func__); ++ return ret_val; ++} ++ ++static int snd_merr_dpcm_remove(struct platform_device *pdev) ++{ ++ struct snd_soc_card *soc_card = platform_get_drvdata(pdev); ++ pr_err("snd_merr_dpcm_remove"); ++ snd_soc_unregister_card(soc_card); ++ platform_set_drvdata(pdev, NULL); ++ return 0; ++} ++ ++const struct dev_pm_ops snd_merr_dpcm_mc_pm_ops = { ++ .prepare = snd_merr_dpcm_prepare, ++ .complete = snd_merr_dpcm_complete, ++ .poweroff = snd_merr_dpcm_poweroff, ++}; ++ ++static struct platform_driver snd_merr_dpcm_drv = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "merr_dpcm_dummy", ++ .pm = &snd_merr_dpcm_mc_pm_ops, ++ }, ++ .probe = snd_merr_dpcm_probe, ++ .remove = snd_merr_dpcm_remove, ++}; ++ ++static int __init snd_merr_dpcm_dummy_init(void) ++{ ++ int err; ++ struct platform_device *device; ++ ++ /* Check from cmdline if hardware is plugged to the board */ ++ if (wm_hardware_avail) { ++ pr_info("dpcm dummy is not supported according to cmdline"); ++ return -EINVAL; ++ } ++ ++ err = platform_driver_register(&snd_merr_dpcm_drv); ++ if (err < 0) ++ return err; ++ ++ device = platform_device_register_simple("merr_dpcm_dummy", ++ 0, NULL, 0); ++ if (IS_ERR(device)) ++ return -ENODEV; ++ ++ return 0; ++} ++late_initcall(snd_merr_dpcm_dummy_init); ++ ++static void __exit snd_merr_dpcm_dummy_exit(void) ++{ ++ return platform_driver_unregister(&snd_merr_dpcm_drv); ++} ++module_exit(snd_merr_dpcm_dummy_exit); ++ ++MODULE_DESCRIPTION("ASoC Intel(R) Edison dummy MID Machine driver"); ++MODULE_AUTHOR("Michael Soares "); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:merr_dpcm_dummy"); ++ +diff --git a/sound/soc/intel/board/merr_dpcm_wm8958.c b/sound/soc/intel/board/merr_dpcm_wm8958.c +new file mode 100644 +index 0000000..9d3aba8 +--- /dev/null ++++ b/sound/soc/intel/board/merr_dpcm_wm8958.c +@@ -0,0 +1,932 @@ ++/* ++ * merr_dpcm_wm8958.c - ASoc DPCM Machine driver for Intel Merrfield MID platform ++ * ++ * Copyright (C) 2013 Intel Corp ++ * Author: Vinod Koul ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include "../../codecs/wm8994.h" ++ ++/* Codec PLL output clk rate */ ++#define CODEC_SYSCLK_RATE 24576000 ++/* Input clock to codec at MCLK1 PIN */ ++#define CODEC_IN_MCLK1_RATE 19200000 ++/* Input clock to codec at MCLK2 PIN */ ++#define CODEC_IN_MCLK2_RATE 32768 ++/* define to select between MCLK1 and MCLK2 input to codec as its clock */ ++#define CODEC_IN_MCLK1 1 ++#define CODEC_IN_MCLK2 2 ++ ++/* Register address for OSC Clock */ ++#define MERR_OSC_CLKOUT_CTRL0_REG_ADDR 0xFF00BC04 ++/* Size of osc clock register */ ++#define MERR_OSC_CLKOUT_CTRL0_REG_SIZE 4 ++ ++struct mrfld_8958_mc_private { ++ struct snd_soc_jack jack; ++ int jack_retry; ++ u8 pmic_id; ++ void __iomem *osc_clk0_reg; ++}; ++ ++ ++/* set_osc_clk0- enable/disables the osc clock0 ++ * addr: address of the register to write to ++ * enable: bool to enable or disable the clock ++ */ ++static inline void set_soc_osc_clk0(void __iomem *addr, bool enable) ++{ ++ u32 osc_clk_ctrl; ++ ++ osc_clk_ctrl = readl(addr); ++ if (enable) ++ osc_clk_ctrl |= BIT(31); ++ else ++ osc_clk_ctrl &= ~(BIT(31)); ++ ++ pr_debug("%s: enable:%d val 0x%x\n", __func__, enable, osc_clk_ctrl); ++ ++ writel(osc_clk_ctrl, addr); ++} ++ ++static inline struct snd_soc_codec *mrfld_8958_get_codec(struct snd_soc_card *card) ++{ ++ bool found = false; ++ struct snd_soc_codec *codec; ++ ++ list_for_each_entry(codec, &card->codec_dev_list, card_list) { ++ if (!strstr(codec->name, "wm8994-codec")) { ++ pr_debug("codec was %s", codec->name); ++ continue; ++ } else { ++ found = true; ++ break; ++ } ++ } ++ if (found == false) { ++ pr_warn("%s: cant find codec", __func__); ++ return NULL; ++ } ++ return codec; ++} ++ ++/* TODO: find better way of doing this */ ++static struct snd_soc_dai *find_codec_dai(struct snd_soc_card *card, const char *dai_name) ++{ ++ int i; ++ for (i = 0; i < card->num_rtd; i++) { ++ if (!strcmp(card->rtd[i].codec_dai->name, dai_name)) ++ return card->rtd[i].codec_dai; ++ } ++ pr_err("%s: unable to find codec dai\n", __func__); ++ /* this should never occur */ ++ WARN_ON(1); ++ return NULL; ++} ++ ++/* Function to switch the input clock for codec, When audio is in ++ * progress input clock to codec will be through MCLK1 which is 19.2MHz ++ * while in off state input clock to codec will be through 32KHz through ++ * MCLK2 ++ * card : Sound card structure ++ * src : Input clock source to codec ++ */ ++static int mrfld_8958_set_codec_clk(struct snd_soc_card *card, int src) ++{ ++ struct snd_soc_dai *aif1_dai = find_codec_dai(card, "wm8994-aif1"); ++ int ret; ++ ++ if (!aif1_dai) ++ return -ENODEV; ++ ++ switch (src) { ++ case CODEC_IN_MCLK1: ++ /* Turn ON the PLL to generate required sysclk rate ++ * from MCLK1 */ ++ ret = snd_soc_dai_set_pll(aif1_dai, ++ WM8994_FLL1, WM8994_FLL_SRC_MCLK1, ++ CODEC_IN_MCLK1_RATE, CODEC_SYSCLK_RATE); ++ if (ret < 0) { ++ pr_err("Failed to start FLL: %d\n", ret); ++ return ret; ++ } ++ /* Switch to MCLK1 input */ ++ ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_FLL1, ++ CODEC_SYSCLK_RATE, SND_SOC_CLOCK_IN); ++ if (ret < 0) { ++ pr_err("Failed to set codec sysclk configuration %d\n", ++ ret); ++ return ret; ++ } ++ break; ++ case CODEC_IN_MCLK2: ++ /* Switch to MCLK2 */ ++ ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2, ++ 32768, SND_SOC_CLOCK_IN); ++ if (ret < 0) { ++ pr_err("Failed to switch to MCLK2: %d", ret); ++ return ret; ++ } ++ /* Turn off PLL for MCLK1 */ ++ ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1, 0, 0, 0); ++ if (ret < 0) { ++ pr_err("Failed to stop the FLL: %d", ret); ++ return ret; ++ } ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int mrfld_wm8958_set_clk_fmt(struct snd_soc_dai *codec_dai) ++{ ++ unsigned int fmt; ++ int ret = 0; ++ struct snd_soc_card *card = codec_dai->card; ++ struct mrfld_8958_mc_private *ctx = snd_soc_card_get_drvdata(card); ++ ++ /* Enable the osc clock at start so that it gets settling time */ ++ set_soc_osc_clk0(ctx->osc_clk0_reg, true); ++ ++ pr_err("setting snd_soc_dai_set_tdm_slot\n"); ++ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0, 0, 4, SNDRV_PCM_FORMAT_S24_LE); ++ if (ret < 0) { ++ pr_err("can't set codec pcm format %d\n", ret); ++ return ret; ++ } ++ ++ /* WM8958 slave Mode */ ++ fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF ++ | SND_SOC_DAIFMT_CBS_CFS; ++ ret = snd_soc_dai_set_fmt(codec_dai, fmt); ++ if (ret < 0) { ++ pr_err("can't set codec DAI configuration %d\n", ret); ++ return ret; ++ } ++ ++ /* FIXME: move this to SYS_CLOCK event handler when codec driver ++ * dependency is clean. ++ */ ++ /* Switch to 19.2MHz MCLK1 input clock for codec */ ++ ret = mrfld_8958_set_codec_clk(card, CODEC_IN_MCLK1); ++ ++ return ret; ++} ++ ++static int mrfld_8958_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; ++ ++ if (!strcmp(codec_dai->name, "wm8994-aif1")) ++ return mrfld_wm8958_set_clk_fmt(codec_dai); ++ return 0; ++} ++ ++static int mrfld_wm8958_compr_set_params(struct snd_compr_stream *cstream) ++{ ++ return 0; ++} ++ ++static const struct snd_soc_pcm_stream mrfld_wm8958_dai_params = { ++ .formats = SNDRV_PCM_FMTBIT_S24_LE, ++ .rate_min = 48000, ++ .rate_max = 48000, ++ .channels_min = 2, ++ .channels_max = 2, ++}; ++ ++static int merr_codec_fixup(struct snd_soc_pcm_runtime *rtd, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_interval *rate = hw_param_interval(params, ++ SNDRV_PCM_HW_PARAM_RATE); ++ struct snd_interval *channels = hw_param_interval(params, ++ SNDRV_PCM_HW_PARAM_CHANNELS); ++ ++ pr_debug("Invoked %s for dailink %s\n", __func__, rtd->dai_link->name); ++ ++ /* The DSP will covert the FE rate to 48k, stereo, 24bits */ ++ rate->min = rate->max = 48000; ++ channels->min = channels->max = 2; ++ ++ /* set SSP2 to 24-bit */ ++ snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - ++ SNDRV_PCM_HW_PARAM_FIRST_MASK], ++ SNDRV_PCM_FORMAT_S24_LE); ++ return 0; ++} ++ ++static int mrfld_8958_set_bias_level(struct snd_soc_card *card, ++ struct snd_soc_dapm_context *dapm, ++ enum snd_soc_bias_level level) ++{ ++ struct snd_soc_dai *aif1_dai = find_codec_dai(card, "wm8994-aif1"); ++ int ret = 0; ++ ++ if (!aif1_dai) ++ return -ENODEV; ++ ++ if (dapm->dev != aif1_dai->dev) ++ return 0; ++ switch (level) { ++ case SND_SOC_BIAS_PREPARE: ++ if (card->dapm.bias_level == SND_SOC_BIAS_STANDBY) ++ ++ ret = mrfld_wm8958_set_clk_fmt(aif1_dai); ++ break; ++ default: ++ break; ++ } ++ pr_debug("%s card(%s)->bias_level %u\n", __func__, card->name, ++ card->dapm.bias_level); ++ return ret; ++} ++ ++static int mrfld_8958_set_bias_level_post(struct snd_soc_card *card, ++ struct snd_soc_dapm_context *dapm, ++ enum snd_soc_bias_level level) ++{ ++ struct snd_soc_dai *aif1_dai = find_codec_dai(card, "wm8994-aif1"); ++ struct mrfld_8958_mc_private *ctx = snd_soc_card_get_drvdata(card); ++ int ret = 0; ++ ++ if (!aif1_dai) ++ return -ENODEV; ++ ++ if (dapm->dev != aif1_dai->dev) ++ return 0; ++ ++ switch (level) { ++ case SND_SOC_BIAS_STANDBY: ++ /* We are in stabdba down so */ ++ /* Switch to 32KHz MCLK2 input clock for codec ++ */ ++ ret = mrfld_8958_set_codec_clk(card, CODEC_IN_MCLK2); ++ /* Turn off 19.2MHz soc osc clock */ ++ set_soc_osc_clk0(ctx->osc_clk0_reg, false); ++ break; ++ default: ++ break; ++ } ++ card->dapm.bias_level = level; ++ pr_debug("%s card(%s)->bias_level %u\n", __func__, card->name, ++ card->dapm.bias_level); ++ return ret; ++} ++ ++#define PMIC_ID_ADDR 0x00 ++#define PMIC_CHIP_ID_A0_VAL 0xC0 ++ ++static int mrfld_8958_set_vflex_vsel(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *k, int event) ++{ ++#define VFLEXCNT 0xAB ++#define VFLEXVSEL_5V 0x01 ++#define VFLEXVSEL_B0_VSYS_PT 0x80 /* B0: Vsys pass-through */ ++#define VFLEXVSEL_A0_4P5V 0x41 /* A0: 4.5V */ ++ ++ struct snd_soc_dapm_context *dapm = w->dapm; ++ struct snd_soc_card *card = dapm->card; ++ struct mrfld_8958_mc_private *ctx = snd_soc_card_get_drvdata(card); ++ ++ u8 vflexvsel, pmic_id = ctx->pmic_id; ++ int retval = 0; ++ ++ pr_debug("%s: ON? %d\n", __func__, SND_SOC_DAPM_EVENT_ON(event)); ++ ++ vflexvsel = (pmic_id == PMIC_CHIP_ID_A0_VAL) ? VFLEXVSEL_A0_4P5V : VFLEXVSEL_B0_VSYS_PT; ++ pr_debug("pmic_id %#x vflexvsel %#x\n", pmic_id, ++ SND_SOC_DAPM_EVENT_ON(event) ? VFLEXVSEL_5V : vflexvsel); ++ ++ if (SND_SOC_DAPM_EVENT_ON(event)) ++ retval = intel_scu_ipc_iowrite8(VFLEXCNT, VFLEXVSEL_5V); ++ else if (SND_SOC_DAPM_EVENT_OFF(event)) ++ retval = intel_scu_ipc_iowrite8(VFLEXCNT, vflexvsel); ++ if (retval) ++ pr_err("Error writing to VFLEXCNT register\n"); ++ ++ return retval; ++} ++ ++static const struct snd_soc_dapm_widget widgets[] = { ++ SND_SOC_DAPM_HP("Headphones", NULL), ++ SND_SOC_DAPM_MIC("AMIC", NULL), ++ SND_SOC_DAPM_MIC("DMIC", NULL), ++ SND_SOC_DAPM_SUPPLY("VFLEXCNT", SND_SOC_NOPM, 0, 0, ++ mrfld_8958_set_vflex_vsel, ++ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), ++}; ++ ++static const struct snd_soc_dapm_route map[] = { ++ { "Headphones", NULL, "HPOUT1L" }, ++ { "Headphones", NULL, "HPOUT1R" }, ++ ++ /* saltbay uses 2 DMICs, other configs may use more so change below ++ * accordingly ++ */ ++ { "DMIC1DAT", NULL, "DMIC" }, ++ { "DMIC2DAT", NULL, "DMIC" }, ++ /*{ "DMIC3DAT", NULL, "DMIC" },*/ ++ /*{ "DMIC4DAT", NULL, "DMIC" },*/ ++ ++ /* MICBIAS2 is connected as Bias for AMIC so we link it ++ * here. Also AMIC wires up to IN1LP pin. ++ * DMIC is externally connected to 1.8V rail, so no link rqd. ++ */ ++ { "AMIC", NULL, "MICBIAS2" }, ++ { "IN1LP", NULL, "AMIC" }, ++ ++ /* SWM map link the SWM outs to codec AIF */ ++ { "AIF1 Playback", NULL, "codec_out0" }, ++ { "AIF1 Playback", NULL, "codec_out1" }, ++ { "codec_in0", NULL, "AIF1 Capture" }, ++ { "codec_in1", NULL, "AIF1 Capture" }, ++ ++ { "AIF1 Playback", NULL, "VFLEXCNT" }, ++ { "AIF1 Capture", NULL, "VFLEXCNT" }, ++}; ++ ++static const struct wm8958_micd_rate micdet_rates[] = { ++ { 32768, true, 1, 4 }, ++ { 32768, false, 1, 1 }, ++ { 44100 * 256, true, 7, 10 }, ++ { 44100 * 256, false, 7, 10 }, ++}; ++ ++static void wm8958_custom_micd_set_rate(struct snd_soc_codec *codec) ++{ ++ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); ++ struct wm8994 *control = dev_get_drvdata(codec->dev->parent); ++ int best, i, sysclk, val; ++ bool idle; ++ const struct wm8958_micd_rate *rates; ++ int num_rates; ++ ++ idle = !wm8994->jack_mic; ++ ++ sysclk = snd_soc_read(codec, WM8994_CLOCKING_1); ++ if (sysclk & WM8994_SYSCLK_SRC) ++ sysclk = wm8994->aifclk[1]; ++ else ++ sysclk = wm8994->aifclk[0]; ++ ++ if (control->pdata.micd_rates) { ++ rates = control->pdata.micd_rates; ++ num_rates = control->pdata.num_micd_rates; ++ } else { ++ rates = micdet_rates; ++ num_rates = ARRAY_SIZE(micdet_rates); ++ } ++ ++ best = 0; ++ for (i = 0; i < num_rates; i++) { ++ if (rates[i].idle != idle) ++ continue; ++ if (abs(rates[i].sysclk - sysclk) < ++ abs(rates[best].sysclk - sysclk)) ++ best = i; ++ else if (rates[best].idle != idle) ++ best = i; ++ } ++ ++ val = rates[best].start << WM8958_MICD_BIAS_STARTTIME_SHIFT ++ | rates[best].rate << WM8958_MICD_RATE_SHIFT; ++ ++ dev_dbg(codec->dev, "MICD rate %d,%d for %dHz %s\n", ++ rates[best].start, rates[best].rate, sysclk, ++ idle ? "idle" : "active"); ++ ++ snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, ++ WM8958_MICD_BIAS_STARTTIME_MASK | ++ WM8958_MICD_RATE_MASK, val); ++} ++ ++static void wm8958_custom_mic_id(void *data, u16 status) ++{ ++ struct snd_soc_codec *codec = data; ++ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); ++ ++ dev_dbg(codec->dev, "wm8958 custom mic id called with status %x\n", ++ status); ++ ++ /* Either nothing present or just starting detection */ ++ if (!(status & WM8958_MICD_STS)) { ++ /* If nothing present then clear our statuses */ ++ dev_dbg(codec->dev, "Detected open circuit\n"); ++ ++ schedule_delayed_work(&wm8994->open_circuit_work, ++ msecs_to_jiffies(2500)); ++ return; ++ } ++ ++ schedule_delayed_work(&wm8994->micd_set_custom_rate_work, ++ msecs_to_jiffies(wm8994->wm8994->pdata.micb_en_delay)); ++ ++ /* If the measurement is showing a high impedence we've got a ++ * microphone. ++ */ ++ if (status & 0x600) { ++ dev_dbg(codec->dev, "Detected microphone\n"); ++ ++ wm8994->mic_detecting = false; ++ wm8994->jack_mic = true; ++ ++ snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADSET, ++ SND_JACK_HEADSET); ++ } ++ ++ ++ if (status & 0xfc) { ++ dev_dbg(codec->dev, "Detected headphone\n"); ++ ++ /* Partial inserts of headsets with complete insert ++ * after an indeterminate amount of time require ++ * continouous micdetect enabled (until open circuit ++ * or headset is detected) ++ * */ ++ wm8994->mic_detecting = true; ++ ++ snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADPHONE, ++ SND_JACK_HEADSET); ++ } ++} ++ ++static int mrfld_8958_init(struct snd_soc_pcm_runtime *runtime) ++{ ++ int ret; ++ unsigned int fmt; ++ struct snd_soc_codec *codec; ++ struct snd_soc_card *card = runtime->card; ++ struct snd_soc_dai *aif1_dai = find_codec_dai(card, "wm8994-aif1"); ++ struct mrfld_8958_mc_private *ctx = snd_soc_card_get_drvdata(card); ++ ++ if (!aif1_dai) ++ return -ENODEV; ++ ++ pr_debug("Entry %s\n", __func__); ++ ++ ret = snd_soc_dai_set_tdm_slot(aif1_dai, 0, 0, 4, SNDRV_PCM_FORMAT_S24_LE); ++ if (ret < 0) { ++ pr_err("can't set codec pcm format %d\n", ret); ++ return ret; ++ } ++ ++ /* WM8958 slave Mode */ ++ fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF ++ | SND_SOC_DAIFMT_CBS_CFS; ++ ret = snd_soc_dai_set_fmt(aif1_dai, fmt); ++ if (ret < 0) { ++ pr_err("can't set codec DAI configuration %d\n", ret); ++ return ret; ++ } ++ ++ mrfld_8958_set_bias_level(card, &card->dapm, SND_SOC_BIAS_OFF); ++ card->dapm.idle_bias_off = true; ++ ++ /* these pins are not used in SB config so mark as nc ++ * ++ * LINEOUT1, 2 ++ * IN1R ++ * DMICDAT2 ++ */ ++ snd_soc_dapm_nc_pin(&card->dapm, "DMIC2DAT"); ++ snd_soc_dapm_nc_pin(&card->dapm, "LINEOUT1P"); ++ snd_soc_dapm_nc_pin(&card->dapm, "LINEOUT1N"); ++ snd_soc_dapm_nc_pin(&card->dapm, "LINEOUT2P"); ++ snd_soc_dapm_nc_pin(&card->dapm, "LINEOUT2N"); ++ snd_soc_dapm_nc_pin(&card->dapm, "IN1RN"); ++ snd_soc_dapm_nc_pin(&card->dapm, "IN1RP"); ++ ++ snd_soc_dapm_sync(&card->dapm); ++ ++ codec = mrfld_8958_get_codec(card); ++ if (!codec) { ++ pr_err("%s: we didnt find the codec pointer!\n", __func__); ++ return 0; ++ } ++ ++ ctx->jack_retry = 0; ++ ret = snd_soc_jack_new(codec, "Intel MID Audio Jack", ++ SND_JACK_HEADSET | SND_JACK_HEADPHONE | ++ SND_JACK_BTN_0 | SND_JACK_BTN_1, ++ &ctx->jack); ++ if (ret) { ++ pr_err("jack creation failed\n"); ++ return ret; ++ } ++ ++ snd_jack_set_key(ctx->jack.jack, SND_JACK_BTN_1, KEY_MEDIA); ++ snd_jack_set_key(ctx->jack.jack, SND_JACK_BTN_0, KEY_MEDIA); ++ ++ wm8958_mic_detect(codec, &ctx->jack, NULL, NULL, ++ wm8958_custom_mic_id, codec); ++ ++ wm8958_micd_set_custom_rate(codec, wm8958_custom_micd_set_rate, codec); ++ ++ snd_soc_update_bits(codec, WM8994_AIF1_DAC1_FILTERS_1, WM8994_AIF1DAC1_MUTE, 0); ++ snd_soc_update_bits(codec, WM8994_AIF1_DAC2_FILTERS_1, WM8994_AIF1DAC2_MUTE, 0); ++ ++ /* Micbias1 is always off, so for pm optimizations make sure the micbias1 ++ * discharge bit is set to floating to avoid discharge in disable state ++ */ ++ snd_soc_update_bits(codec, WM8958_MICBIAS1, WM8958_MICB1_DISCH, 0); ++ ++ return 0; ++} ++ ++static unsigned int rates_8000_16000[] = { ++ 8000, ++ 16000, ++}; ++ ++static struct snd_pcm_hw_constraint_list constraints_8000_16000 = { ++ .count = ARRAY_SIZE(rates_8000_16000), ++ .list = rates_8000_16000, ++}; ++static unsigned int rates_48000[] = { ++ 48000, ++}; ++ ++static struct snd_pcm_hw_constraint_list constraints_48000 = { ++ .count = ARRAY_SIZE(rates_48000), ++ .list = rates_48000, ++}; ++static int mrfld_8958_startup(struct snd_pcm_substream *substream) ++{ ++ return snd_pcm_hw_constraint_list(substream->runtime, 0, ++ SNDRV_PCM_HW_PARAM_RATE, ++ &constraints_48000); ++} ++ ++static struct snd_soc_ops mrfld_8958_ops = { ++ .startup = mrfld_8958_startup, ++}; ++static int mrfld_8958_8k_16k_startup(struct snd_pcm_substream *substream) ++{ ++ return snd_pcm_hw_constraint_list(substream->runtime, 0, ++ SNDRV_PCM_HW_PARAM_RATE, ++ &constraints_8000_16000); ++} ++ ++static struct snd_soc_ops mrfld_8958_8k_16k_ops = { ++ .startup = mrfld_8958_8k_16k_startup, ++ .hw_params = mrfld_8958_hw_params, ++}; ++ ++static struct snd_soc_ops mrfld_8958_be_ssp2_ops = { ++ .hw_params = mrfld_8958_hw_params, ++}; ++static struct snd_soc_compr_ops mrfld_compr_ops = { ++ .set_params = mrfld_wm8958_compr_set_params, ++}; ++ ++struct snd_soc_dai_link mrfld_8958_msic_dailink[] = { ++ [MERR_DPCM_AUDIO] = { ++ .name = "Merrifield Audio Port", ++ .stream_name = "Saltbay Audio", ++ .cpu_dai_name = "Headset-cpu-dai", ++ .codec_name = "snd-soc-dummy", ++ .codec_dai_name = "snd-soc-dummy-dai", ++ .platform_name = "sst-platform", ++ .init = mrfld_8958_init, ++ .ignore_suspend = 1, ++ .dynamic = 1, ++ .ops = &mrfld_8958_ops, ++ }, ++ [MERR_DPCM_DB] = { ++ .name = "Merrifield DB Audio Port", ++ .stream_name = "Deep Buffer Audio", ++ .cpu_dai_name = "Deepbuffer-cpu-dai", ++ .codec_name = "snd-soc-dummy", ++ .codec_dai_name = "snd-soc-dummy-dai", ++ .platform_name = "sst-platform", ++ .init = mrfld_8958_init, ++ .ignore_suspend = 1, ++ .dynamic = 1, ++ .ops = &mrfld_8958_ops, ++ }, ++ [MERR_DPCM_LL] = { ++ .name = "Merrifield LL Audio Port", ++ .stream_name = "Low Latency Audio", ++ .cpu_dai_name = "Lowlatency-cpu-dai", ++ .codec_name = "snd-soc-dummy", ++ .codec_dai_name = "snd-soc-dummy-dai", ++ .platform_name = "sst-platform", ++ .init = mrfld_8958_init, ++ .ignore_suspend = 1, ++ .dynamic = 1, ++ .ops = &mrfld_8958_ops, ++ }, ++ [MERR_DPCM_COMPR] = { ++ .name = "Merrifield Compress Port", ++ .stream_name = "Saltbay Compress", ++ .platform_name = "sst-platform", ++ .cpu_dai_name = "Compress-cpu-dai", ++ .codec_name = "snd-soc-dummy", ++ .codec_dai_name = "snd-soc-dummy-dai", ++ .dynamic = 1, ++ .init = mrfld_8958_init, ++ .compr_ops = &mrfld_compr_ops, ++ }, ++ [MERR_DPCM_VOIP] = { ++ .name = "Merrifield VOIP Port", ++ .stream_name = "Saltbay Voip", ++ .cpu_dai_name = "Voip-cpu-dai", ++ .platform_name = "sst-platform", ++ .codec_dai_name = "snd-soc-dummy-dai", ++ .codec_name = "snd-soc-dummy", ++ .init = NULL, ++ .ignore_suspend = 1, ++ .ops = &mrfld_8958_8k_16k_ops, ++ .dynamic = 1, ++ }, ++ [MERR_DPCM_PROBE] = { ++ .name = "Merrifield Probe Port", ++ .stream_name = "Saltbay Probe", ++ .cpu_dai_name = "Probe-cpu-dai", ++ .codec_dai_name = "snd-soc-dummy-dai", ++ .codec_name = "snd-soc-dummy", ++ .platform_name = "sst-platform", ++ .playback_count = 8, ++ .capture_count = 8, ++ }, ++ /* CODEC<->CODEC link */ ++ { ++ .name = "Merrifield Codec-Loop Port", ++ .stream_name = "Saltbay Codec-Loop", ++ .cpu_dai_name = "snd-soc-dummy-dai", ++ .codec_dai_name = "wm8994-aif1", ++ .codec_name = "wm8994-codec", ++ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF ++ | SND_SOC_DAIFMT_CBS_CFS, ++ .params = &mrfld_wm8958_dai_params, ++ }, ++ ++ /* back ends */ ++ { ++ .name = "SSP2-Codec", ++ .be_id = 1, ++ .cpu_dai_name = "ssp2-codec", ++ .platform_name = "sst-platform", ++ .no_pcm = 1, ++ .codec_dai_name = "wm8994-aif1", ++ .codec_name = "wm8994-codec", ++ .be_hw_params_fixup = merr_codec_fixup, ++ .ignore_suspend = 1, ++ .ops = &mrfld_8958_be_ssp2_ops, ++ }, ++ { ++ .name = "SSP1-BTFM", ++ .be_id = 2, ++ .cpu_dai_name = "snd-soc-dummy-dai", ++ .platform_name = "snd-soc-dummy", ++ .no_pcm = 1, ++ .codec_name = "snd-soc-dummy", ++ .codec_dai_name = "snd-soc-dummy-dai", ++ .ignore_suspend = 1, ++ }, ++ { ++ .name = "SSP0-Modem", ++ .be_id = 3, ++ .cpu_dai_name = "snd-soc-dummy-dai", ++ .platform_name = "snd-soc-dummy", ++ .no_pcm = 1, ++ .codec_name = "snd-soc-dummy", ++ .codec_dai_name = "snd-soc-dummy-dai", ++ .ignore_suspend = 1, ++ }, ++}; ++ ++#ifdef CONFIG_PM_SLEEP ++static int snd_mrfld_8958_prepare(struct device *dev) ++{ ++ pr_debug("In %s device name\n", __func__); ++ snd_soc_suspend(dev); ++ return 0; ++} ++ ++static void snd_mrfld_8958_complete(struct device *dev) ++{ ++ pr_debug("In %s\n", __func__); ++ snd_soc_resume(dev); ++ return; ++} ++ ++static int snd_mrfld_8958_poweroff(struct device *dev) ++{ ++ pr_debug("In %s\n", __func__); ++ snd_soc_poweroff(dev); ++ return 0; ++} ++#else ++#define snd_mrfld_8958_prepare NULL ++#define snd_mrfld_8958_complete NULL ++#define snd_mrfld_8958_poweroff NULL ++#endif ++ ++/* SoC card */ ++static struct snd_soc_card snd_soc_card_mrfld = { ++ .name = "wm8958-audio", ++ .dai_link = mrfld_8958_msic_dailink, ++ .num_links = ARRAY_SIZE(mrfld_8958_msic_dailink), ++ .set_bias_level = mrfld_8958_set_bias_level, ++ .set_bias_level_post = mrfld_8958_set_bias_level_post, ++ .dapm_widgets = widgets, ++ .num_dapm_widgets = ARRAY_SIZE(widgets), ++ .dapm_routes = map, ++ .num_dapm_routes = ARRAY_SIZE(map), ++}; ++ ++static int snd_mrfld_8958_mc_probe(struct platform_device *pdev) ++{ ++ int ret_val = 0; ++ struct mrfld_8958_mc_private *drv; ++ ++ pr_debug("Entry %s\n", __func__); ++ ++ drv = kzalloc(sizeof(*drv), GFP_ATOMIC); ++ if (!drv) { ++ pr_err("allocation failed\n"); ++ return -ENOMEM; ++ } ++ ++ /* ioremap the register */ ++ drv->osc_clk0_reg = devm_ioremap_nocache(&pdev->dev, ++ MERR_OSC_CLKOUT_CTRL0_REG_ADDR, ++ MERR_OSC_CLKOUT_CTRL0_REG_SIZE); ++ if (!drv->osc_clk0_reg) { ++ pr_err("osc clk0 ctrl ioremap failed\n"); ++ ret_val = -1; ++ goto unalloc; ++ } ++ ++ ret_val = intel_scu_ipc_ioread8(PMIC_ID_ADDR, &drv->pmic_id); ++ if (ret_val) { ++ pr_err("Error reading PMIC ID register\n"); ++ goto unalloc; ++ } ++ ++ /* register the soc card */ ++ snd_soc_card_mrfld.dev = &pdev->dev; ++ snd_soc_card_set_drvdata(&snd_soc_card_mrfld, drv); ++ ret_val = snd_soc_register_card(&snd_soc_card_mrfld); ++ if (ret_val) { ++ pr_err("snd_soc_register_card failed %d\n", ret_val); ++ goto unalloc; ++ } ++ platform_set_drvdata(pdev, &snd_soc_card_mrfld); ++ pr_info("%s successful\n", __func__); ++ return ret_val; ++ ++unalloc: ++ kfree(drv); ++ return ret_val; ++} ++ ++static int snd_mrfld_8958_mc_remove(struct platform_device *pdev) ++{ ++ struct snd_soc_card *soc_card = platform_get_drvdata(pdev); ++ struct mrfld_8958_mc_private *drv = snd_soc_card_get_drvdata(soc_card); ++ ++ pr_debug("In %s\n", __func__); ++ kfree(drv); ++ snd_soc_card_set_drvdata(soc_card, NULL); ++ snd_soc_unregister_card(soc_card); ++ platform_set_drvdata(pdev, NULL); ++ return 0; ++} ++ ++const struct dev_pm_ops snd_mrfld_8958_mc_pm_ops = { ++ .prepare = snd_mrfld_8958_prepare, ++ .complete = snd_mrfld_8958_complete, ++ .poweroff = snd_mrfld_8958_poweroff, ++}; ++ ++static struct platform_driver snd_mrfld_8958_mc_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "mrfld_wm8958", ++ .pm = &snd_mrfld_8958_mc_pm_ops, ++ }, ++ .probe = snd_mrfld_8958_mc_probe, ++ .remove = snd_mrfld_8958_mc_remove, ++}; ++ ++static int snd_mrfld_8958_driver_init(void) ++{ ++ pr_info("Merrifield Machine Driver mrfld_wm8958 registerd\n"); ++ return platform_driver_register(&snd_mrfld_8958_mc_driver); ++} ++ ++static void snd_mrfld_8958_driver_exit(void) ++{ ++ pr_debug("In %s\n", __func__); ++ platform_driver_unregister(&snd_mrfld_8958_mc_driver); ++} ++ ++static int snd_mrfld_8958_rpmsg_probe(struct rpmsg_channel *rpdev) ++{ ++ int ret = 0; ++ ++ if (rpdev == NULL) { ++ pr_err("rpmsg channel not created\n"); ++ ret = -ENODEV; ++ goto out; ++ } ++ ++ dev_info(&rpdev->dev, "Probed snd_mrfld wm8958 rpmsg device\n"); ++ ++ ret = snd_mrfld_8958_driver_init(); ++ ++out: ++ return ret; ++} ++ ++static void snd_mrfld_8958_rpmsg_remove(struct rpmsg_channel *rpdev) ++{ ++ snd_mrfld_8958_driver_exit(); ++ dev_info(&rpdev->dev, "Removed snd_mrfld wm8958 rpmsg device\n"); ++} ++ ++static void snd_mrfld_8958_rpmsg_cb(struct rpmsg_channel *rpdev, void *data, ++ int len, void *priv, u32 src) ++{ ++ dev_warn(&rpdev->dev, "unexpected, message\n"); ++ ++ print_hex_dump(KERN_DEBUG, __func__, DUMP_PREFIX_NONE, 16, 1, ++ data, len, true); ++} ++ ++static struct rpmsg_device_id snd_mrfld_8958_rpmsg_id_table[] = { ++ { .name = "rpmsg_mrfld_wm8958_audio" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(rpmsg, snd_mrfld_8958_rpmsg_id_table); ++ ++static struct rpmsg_driver snd_mrfld_8958_rpmsg = { ++ .drv.name = KBUILD_MODNAME, ++ .drv.owner = THIS_MODULE, ++ .id_table = snd_mrfld_8958_rpmsg_id_table, ++ .probe = snd_mrfld_8958_rpmsg_probe, ++ .callback = snd_mrfld_8958_rpmsg_cb, ++ .remove = snd_mrfld_8958_rpmsg_remove, ++}; ++ ++static int __init snd_mrfld_8958_rpmsg_init(void) ++{ ++ return register_rpmsg_driver(&snd_mrfld_8958_rpmsg); ++} ++late_initcall(snd_mrfld_8958_rpmsg_init); ++ ++static void __exit snd_mrfld_8958_rpmsg_exit(void) ++{ ++ return unregister_rpmsg_driver(&snd_mrfld_8958_rpmsg); ++} ++module_exit(snd_mrfld_8958_rpmsg_exit); ++ ++MODULE_DESCRIPTION("ASoC Intel(R) Merrifield MID Machine driver"); ++MODULE_AUTHOR("Vinod Koul "); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:mrfld_wm8958"); +diff --git a/sound/soc/intel/board/merr_saltbay_wm8958.c b/sound/soc/intel/board/merr_saltbay_wm8958.c +new file mode 100644 +index 0000000..ef63154 +--- /dev/null ++++ b/sound/soc/intel/board/merr_saltbay_wm8958.c +@@ -0,0 +1,887 @@ ++/* ++ * merr_saltbay_wm8958.c - ASoc Machine driver for Intel Merrfield MID platform ++ * ++ * Copyright (C) 2013 Intel Corp ++ * Author: Vinod Koul ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include "../../codecs/wm8994.h" ++ ++/* Codec PLL output clk rate */ ++#define CODEC_SYSCLK_RATE 24576000 ++/* Input clock to codec at MCLK1 PIN */ ++#define CODEC_IN_MCLK1_RATE 19200000 ++/* Input clock to codec at MCLK2 PIN */ ++#define CODEC_IN_MCLK2_RATE 32768 ++/* define to select between MCLK1 and MCLK2 input to codec as its clock */ ++#define CODEC_IN_MCLK1 1 ++#define CODEC_IN_MCLK2 2 ++ ++/* Register address for OSC Clock */ ++#define MERR_OSC_CLKOUT_CTRL0_REG_ADDR 0xFF00BC04 ++/* Size of osc clock register */ ++#define MERR_OSC_CLKOUT_CTRL0_REG_SIZE 4 ++ ++struct mrfld_8958_mc_private { ++ struct snd_soc_jack jack; ++ int jack_retry; ++ u8 pmic_id; ++ void __iomem *osc_clk0_reg; ++}; ++ ++ ++/* set_osc_clk0- enable/disables the osc clock0 ++ * addr: address of the register to write to ++ * enable: bool to enable or disable the clock ++ */ ++static inline void set_soc_osc_clk0(void __iomem *addr, bool enable) ++{ ++ u32 osc_clk_ctrl; ++ ++ osc_clk_ctrl = readl(addr); ++ if (enable) ++ osc_clk_ctrl |= BIT(31); ++ else ++ osc_clk_ctrl &= ~(BIT(31)); ++ ++ pr_debug("%s: enable:%d val 0x%x\n", __func__, enable, osc_clk_ctrl); ++ ++ writel(osc_clk_ctrl, addr); ++} ++ ++ ++static inline struct snd_soc_codec *mrfld_8958_get_codec(struct snd_soc_card *card) ++{ ++ bool found = false; ++ struct snd_soc_codec *codec; ++ ++ list_for_each_entry(codec, &card->codec_dev_list, card_list) { ++ if (!strstr(codec->name, "wm8994-codec")) { ++ pr_debug("codec was %s", codec->name); ++ continue; ++ } else { ++ found = true; ++ break; ++ } ++ } ++ if (found == false) { ++ pr_err("%s: cant find codec", __func__); ++ return NULL; ++ } ++ return codec; ++} ++ ++/* Function to switch the input clock for codec, When audio is in ++ * progress input clock to codec will be through MCLK1 which is 19.2MHz ++ * while in off state input clock to codec will be through 32KHz through ++ * MCLK2 ++ * card : Sound card structure ++ * src : Input clock source to codec ++ */ ++static int mrfld_8958_set_codec_clk(struct snd_soc_card *card, int src) ++{ ++ struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai; ++ int ret; ++ ++ switch (src) { ++ case CODEC_IN_MCLK1: ++ /* Turn ON the PLL to generate required sysclk rate ++ * from MCLK1 */ ++ ret = snd_soc_dai_set_pll(aif1_dai, ++ WM8994_FLL1, WM8994_FLL_SRC_MCLK1, ++ CODEC_IN_MCLK1_RATE, CODEC_SYSCLK_RATE); ++ if (ret < 0) { ++ pr_err("Failed to start FLL: %d\n", ret); ++ return ret; ++ } ++ /* Switch to MCLK1 input */ ++ ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_FLL1, ++ CODEC_SYSCLK_RATE, SND_SOC_CLOCK_IN); ++ if (ret < 0) { ++ pr_err("Failed to set codec sysclk configuration %d\n", ++ ret); ++ return ret; ++ } ++ break; ++ case CODEC_IN_MCLK2: ++ /* Switch to MCLK2 */ ++ ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2, ++ 32768, SND_SOC_CLOCK_IN); ++ if (ret < 0) { ++ pr_err("Failed to switch to MCLK2: %d", ret); ++ return ret; ++ } ++ /* Turn off PLL for MCLK1 */ ++ ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1, 0, 0, 0); ++ if (ret < 0) { ++ pr_err("Failed to stop the FLL: %d", ret); ++ return ret; ++ } ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int mrfld_wm8958_set_clk_fmt(struct snd_soc_dai *codec_dai) ++{ ++ unsigned int fmt; ++ int ret = 0; ++ struct snd_soc_card *card = codec_dai->card; ++ struct mrfld_8958_mc_private *ctx = snd_soc_card_get_drvdata(card); ++ ++ /* Enable the osc clock at start so that it gets settling time */ ++ set_soc_osc_clk0(ctx->osc_clk0_reg, true); ++ ++ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0, 0, 4, SNDRV_PCM_FORMAT_S24_LE); ++ if (ret < 0) { ++ pr_err("can't set codec pcm format %d\n", ret); ++ return ret; ++ } ++ ++ /* WM8958 slave Mode */ ++ fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF ++ | SND_SOC_DAIFMT_CBS_CFS; ++ ret = snd_soc_dai_set_fmt(codec_dai, fmt); ++ if (ret < 0) { ++ pr_err("can't set codec DAI configuration %d\n", ret); ++ return ret; ++ } ++ ++ /* FIXME: move this to SYS_CLOCK event handler when codec driver ++ * dependency is clean. ++ */ ++ /* Switch to 19.2MHz MCLK1 input clock for codec */ ++ ret = mrfld_8958_set_codec_clk(card, CODEC_IN_MCLK1); ++ ++ return ret; ++} ++ ++static int mrfld_8958_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; ++ ++ return mrfld_wm8958_set_clk_fmt(codec_dai); ++} ++ ++static int mrfld_wm8958_compr_set_params(struct snd_compr_stream *cstream) ++{ ++ struct snd_soc_pcm_runtime *rtd = cstream->private_data; ++ struct snd_soc_dai *codec_dai = rtd->codec_dai; ++ ++ return mrfld_wm8958_set_clk_fmt(codec_dai); ++} ++static int mrfld_8958_set_bias_level(struct snd_soc_card *card, ++ struct snd_soc_dapm_context *dapm, ++ enum snd_soc_bias_level level) ++{ ++ struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai; ++ int ret = 0; ++ ++ if (dapm->dev != aif1_dai->dev) ++ return 0; ++ switch (level) { ++ case SND_SOC_BIAS_PREPARE: ++ if (card->dapm.bias_level == SND_SOC_BIAS_STANDBY) ++ ++ ret = mrfld_wm8958_set_clk_fmt(aif1_dai); ++ break; ++ default: ++ break; ++ } ++ pr_debug("%s card(%s)->bias_level %u\n", __func__, card->name, ++ card->dapm.bias_level); ++ return ret; ++} ++static int mrfld_8958_set_bias_level_post(struct snd_soc_card *card, ++ struct snd_soc_dapm_context *dapm, ++ enum snd_soc_bias_level level) ++{ ++ struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai; ++ struct mrfld_8958_mc_private *ctx = snd_soc_card_get_drvdata(card); ++ int ret = 0; ++ ++ if (dapm->dev != aif1_dai->dev) ++ return 0; ++ ++ switch (level) { ++ case SND_SOC_BIAS_STANDBY: ++ /* We are in stabdba down so */ ++ /* Switch to 32KHz MCLK2 input clock for codec ++ */ ++ ret = mrfld_8958_set_codec_clk(card, CODEC_IN_MCLK2); ++ /* Turn off 19.2MHz soc osc clock */ ++ set_soc_osc_clk0(ctx->osc_clk0_reg, false); ++ break; ++ default: ++ break; ++ } ++ card->dapm.bias_level = level; ++ pr_debug("%s card(%s)->bias_level %u\n", __func__, card->name, ++ card->dapm.bias_level); ++ return ret; ++} ++ ++#define PMIC_ID_ADDR 0x00 ++#define PMIC_CHIP_ID_A0_VAL 0xC0 ++ ++static int mrfld_8958_set_vflex_vsel(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *k, int event) ++{ ++#define VFLEXCNT 0xAB ++#define VFLEXVSEL_5V 0x01 ++#define VFLEXVSEL_B0_VSYS_PT 0x80 /* B0: Vsys pass-through */ ++#define VFLEXVSEL_A0_4P5V 0x41 /* A0: 4.5V */ ++ ++ struct snd_soc_dapm_context *dapm = w->dapm; ++ struct snd_soc_card *card = dapm->card; ++ struct mrfld_8958_mc_private *ctx = snd_soc_card_get_drvdata(card); ++ ++ u8 vflexvsel, pmic_id = ctx->pmic_id; ++ int retval = 0; ++ ++ pr_debug("%s: ON? %d\n", __func__, SND_SOC_DAPM_EVENT_ON(event)); ++ ++ vflexvsel = (pmic_id == PMIC_CHIP_ID_A0_VAL) ? VFLEXVSEL_A0_4P5V : VFLEXVSEL_B0_VSYS_PT; ++ pr_debug("pmic_id %#x vflexvsel %#x\n", pmic_id, ++ SND_SOC_DAPM_EVENT_ON(event) ? VFLEXVSEL_5V : vflexvsel); ++ ++ /*FIXME: seems to be issue with bypass mode in MOOR, for now ++ force the bias off volate as VFLEXVSEL_5V */ ++ if ((INTEL_MID_BOARD(1, PHONE, MOFD)) || ++ (INTEL_MID_BOARD(1, TABLET, MOFD))) ++ vflexvsel = VFLEXVSEL_5V; ++ ++ if (SND_SOC_DAPM_EVENT_ON(event)) ++ retval = intel_scu_ipc_iowrite8(VFLEXCNT, VFLEXVSEL_5V); ++ else if (SND_SOC_DAPM_EVENT_OFF(event)) ++ retval = intel_scu_ipc_iowrite8(VFLEXCNT, vflexvsel); ++ if (retval) ++ pr_err("Error writing to VFLEXCNT register\n"); ++ ++ return retval; ++} ++ ++static const struct snd_soc_dapm_widget widgets[] = { ++ SND_SOC_DAPM_HP("Headphones", NULL), ++ SND_SOC_DAPM_MIC("AMIC", NULL), ++ SND_SOC_DAPM_MIC("DMIC", NULL), ++ SND_SOC_DAPM_SUPPLY("VFLEXCNT", SND_SOC_NOPM, 0, 0, ++ mrfld_8958_set_vflex_vsel, ++ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), ++}; ++ ++static const struct snd_soc_dapm_route map[] = { ++ { "Headphones", NULL, "HPOUT1L" }, ++ { "Headphones", NULL, "HPOUT1R" }, ++ ++ /* saltbay uses 2 DMICs, other configs may use more so change below ++ * accordingly ++ */ ++ { "DMIC1DAT", NULL, "DMIC" }, ++ { "DMIC2DAT", NULL, "DMIC" }, ++ /*{ "DMIC3DAT", NULL, "DMIC" },*/ ++ /*{ "DMIC4DAT", NULL, "DMIC" },*/ ++ ++ /* MICBIAS2 is connected as Bias for AMIC so we link it ++ * here. Also AMIC wires up to IN1LP pin. ++ * DMIC is externally connected to 1.8V rail, so no link rqd. ++ */ ++ { "AMIC", NULL, "MICBIAS2" }, ++ { "IN1LP", NULL, "AMIC" }, ++ ++ /* SWM map link the SWM outs to codec AIF */ ++ { "AIF1DAC1L", NULL, "Codec OUT0" }, ++ { "AIF1DAC1R", NULL, "Codec OUT0" }, ++ { "AIF1DAC2L", NULL, "Codec OUT1" }, ++ { "AIF1DAC2R", NULL, "Codec OUT1" }, ++ { "Codec IN0", NULL, "AIF1ADC1L" }, ++ { "Codec IN0", NULL, "AIF1ADC1R" }, ++ { "Codec IN1", NULL, "AIF1ADC1L" }, ++ { "Codec IN1", NULL, "AIF1ADC1R" }, ++ ++ { "AIF1DAC1L", NULL, "VFLEXCNT" }, ++ { "AIF1DAC1R", NULL, "VFLEXCNT" }, ++ { "AIF1DAC2L", NULL, "VFLEXCNT" }, ++ { "AIF1DAC2R", NULL, "VFLEXCNT" }, ++ ++ { "AIF1ADC1L", NULL, "VFLEXCNT" }, ++ { "AIF1ADC1R", NULL, "VFLEXCNT" }, ++ ++}; ++ ++static const struct wm8958_micd_rate micdet_rates[] = { ++ { 32768, true, 1, 4 }, ++ { 32768, false, 1, 1 }, ++ { 44100 * 256, true, 7, 10 }, ++ { 44100 * 256, false, 7, 10 }, ++}; ++ ++static void wm8958_custom_micd_set_rate(struct snd_soc_codec *codec) ++{ ++ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); ++ struct wm8994 *control = dev_get_drvdata(codec->dev->parent); ++ int best, i, sysclk, val; ++ bool idle; ++ const struct wm8958_micd_rate *rates; ++ int num_rates; ++ ++ idle = !wm8994->jack_mic; ++ ++ sysclk = snd_soc_read(codec, WM8994_CLOCKING_1); ++ if (sysclk & WM8994_SYSCLK_SRC) ++ sysclk = wm8994->aifclk[1]; ++ else ++ sysclk = wm8994->aifclk[0]; ++ ++ if (control->pdata.micd_rates) { ++ rates = control->pdata.micd_rates; ++ num_rates = control->pdata.num_micd_rates; ++ } else { ++ rates = micdet_rates; ++ num_rates = ARRAY_SIZE(micdet_rates); ++ } ++ ++ best = 0; ++ for (i = 0; i < num_rates; i++) { ++ if (rates[i].idle != idle) ++ continue; ++ if (abs(rates[i].sysclk - sysclk) < ++ abs(rates[best].sysclk - sysclk)) ++ best = i; ++ else if (rates[best].idle != idle) ++ best = i; ++ } ++ ++ val = rates[best].start << WM8958_MICD_BIAS_STARTTIME_SHIFT ++ | rates[best].rate << WM8958_MICD_RATE_SHIFT; ++ ++ dev_dbg(codec->dev, "MICD rate %d,%d for %dHz %s\n", ++ rates[best].start, rates[best].rate, sysclk, ++ idle ? "idle" : "active"); ++ ++ snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, ++ WM8958_MICD_BIAS_STARTTIME_MASK | ++ WM8958_MICD_RATE_MASK, val); ++} ++ ++static void wm8958_custom_mic_id(void *data, u16 status) ++{ ++ struct snd_soc_codec *codec = data; ++ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); ++ ++ dev_dbg(codec->dev, "wm8958 custom mic id called with status %x\n", ++ status); ++ ++ /* Either nothing present or just starting detection */ ++ if (!(status & WM8958_MICD_STS)) { ++ /* If nothing present then clear our statuses */ ++ dev_dbg(codec->dev, "Detected open circuit\n"); ++ ++ schedule_delayed_work(&wm8994->open_circuit_work, ++ msecs_to_jiffies(2500)); ++ return; ++ } ++ ++ schedule_delayed_work(&wm8994->micd_set_custom_rate_work, ++ msecs_to_jiffies(wm8994->wm8994->pdata.micb_en_delay)); ++ ++ /* If the measurement is showing a high impedence we've got a ++ * microphone. ++ */ ++ if (status & 0x600) { ++ dev_dbg(codec->dev, "Detected microphone\n"); ++ ++ wm8994->mic_detecting = false; ++ wm8994->jack_mic = true; ++ wm8994->headphone_detected = false; ++ ++ snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADSET, ++ SND_JACK_HEADSET); ++ } ++ ++ ++ if (status & 0xfc) { ++ dev_dbg(codec->dev, "Detected headphone\n"); ++ ++ /* Partial inserts of headsets with complete insert ++ * after an indeterminate amount of time require ++ * continouous micdetect enabled (until open circuit ++ * or headset is detected) ++ * */ ++ wm8994->mic_detecting = true; ++ ++ wm8994->jack_mic = false; ++ wm8994->headphone_detected = true; ++ ++ snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADPHONE, ++ SND_JACK_HEADSET); ++ } ++} ++ ++static int mrfld_8958_init(struct snd_soc_pcm_runtime *runtime) ++{ ++ int ret; ++ unsigned int fmt; ++ struct snd_soc_codec *codec = runtime->codec; ++ struct snd_soc_dapm_context *dapm = &codec->dapm; ++ struct snd_soc_card *card = runtime->card; ++ struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai; ++ struct mrfld_8958_mc_private *ctx = snd_soc_card_get_drvdata(card); ++ ++ pr_debug("Entry %s\n", __func__); ++ ++ ret = snd_soc_dai_set_tdm_slot(aif1_dai, 0, 0, 4, SNDRV_PCM_FORMAT_S24_LE); ++ if (ret < 0) { ++ pr_err("can't set codec pcm format %d\n", ret); ++ return ret; ++ } ++ ++ /* WM8958 slave Mode */ ++ fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF ++ | SND_SOC_DAIFMT_CBS_CFS; ++ ret = snd_soc_dai_set_fmt(aif1_dai, fmt); ++ if (ret < 0) { ++ pr_err("can't set codec DAI configuration %d\n", ret); ++ return ret; ++ } ++ ++ mrfld_8958_set_bias_level(card, dapm, SND_SOC_BIAS_OFF); ++ card->dapm.idle_bias_off = true; ++ ++ /* these pins are not used in SB config so mark as nc ++ * ++ * LINEOUT1, 2 ++ * IN1R ++ * DMICDAT2 ++ */ ++ snd_soc_dapm_nc_pin(dapm, "DMIC2DAT"); ++ snd_soc_dapm_nc_pin(dapm, "LINEOUT1P"); ++ snd_soc_dapm_nc_pin(dapm, "LINEOUT1N"); ++ snd_soc_dapm_nc_pin(dapm, "LINEOUT2P"); ++ snd_soc_dapm_nc_pin(dapm, "LINEOUT2N"); ++ snd_soc_dapm_nc_pin(dapm, "IN1RN"); ++ snd_soc_dapm_nc_pin(dapm, "IN1RP"); ++ ++ /* Force enable VMID to avoid cold latency constraints */ ++ snd_soc_dapm_force_enable_pin(dapm, "VMID"); ++ snd_soc_dapm_sync(dapm); ++ ++ ctx->jack_retry = 0; ++ ret = snd_soc_jack_new(codec, "Intel MID Audio Jack", ++ SND_JACK_HEADSET | SND_JACK_HEADPHONE | ++ SND_JACK_BTN_0 | SND_JACK_BTN_1, ++ &ctx->jack); ++ if (ret) { ++ pr_err("jack creation failed\n"); ++ return ret; ++ } ++ ++ snd_jack_set_key(ctx->jack.jack, SND_JACK_BTN_1, KEY_MEDIA); ++ snd_jack_set_key(ctx->jack.jack, SND_JACK_BTN_0, KEY_MEDIA); ++ ++ wm8958_mic_detect(codec, &ctx->jack, NULL, NULL, ++ wm8958_custom_mic_id, codec); ++ ++ wm8958_micd_set_custom_rate(codec, wm8958_custom_micd_set_rate, codec); ++ ++ snd_soc_update_bits(codec, WM8994_AIF1_DAC1_FILTERS_1, WM8994_AIF1DAC1_MUTE, 0); ++ snd_soc_update_bits(codec, WM8994_AIF1_DAC2_FILTERS_1, WM8994_AIF1DAC2_MUTE, 0); ++ ++ /* Micbias1 is always off, so for pm optimizations make sure the micbias1 ++ * discharge bit is set to floating to avoid discharge in disable state ++ */ ++ snd_soc_update_bits(codec, WM8958_MICBIAS1, WM8958_MICB1_DISCH, 0); ++ ++ return 0; ++} ++ ++static unsigned int rates_8000_16000[] = { ++ 8000, ++ 16000, ++}; ++ ++static struct snd_pcm_hw_constraint_list constraints_8000_16000 = { ++ .count = ARRAY_SIZE(rates_8000_16000), ++ .list = rates_8000_16000, ++}; ++ ++static unsigned int rates_48000[] = { ++ 48000, ++}; ++ ++static struct snd_pcm_hw_constraint_list constraints_48000 = { ++ .count = ARRAY_SIZE(rates_48000), ++ .list = rates_48000, ++}; ++ ++static int mrfld_8958_startup(struct snd_pcm_substream *substream) ++{ ++ return snd_pcm_hw_constraint_list(substream->runtime, 0, ++ SNDRV_PCM_HW_PARAM_RATE, ++ &constraints_48000); ++} ++ ++static struct snd_soc_ops mrfld_8958_ops = { ++ .startup = mrfld_8958_startup, ++ .hw_params = mrfld_8958_hw_params, ++}; ++ ++static int mrfld_8958_8k_16k_startup(struct snd_pcm_substream *substream) ++{ ++ return snd_pcm_hw_constraint_list(substream->runtime, 0, ++ SNDRV_PCM_HW_PARAM_RATE, ++ &constraints_8000_16000); ++} ++ ++static struct snd_soc_ops mrfld_8958_8k_16k_ops = { ++ .startup = mrfld_8958_8k_16k_startup, ++ .hw_params = mrfld_8958_hw_params, ++}; ++ ++static struct snd_soc_compr_ops mrfld_compr_ops = { ++ .set_params = mrfld_wm8958_compr_set_params, ++}; ++ ++struct snd_soc_dai_link mrfld_8958_msic_dailink[] = { ++ [MERR_SALTBAY_AUDIO] = { ++ .name = "Merrifield Audio Port", ++ .stream_name = "Audio", ++ .cpu_dai_name = "Headset-cpu-dai", ++ .codec_dai_name = "wm8994-aif1", ++ .codec_name = "wm8994-codec", ++ .platform_name = "sst-platform", ++ .init = mrfld_8958_init, ++ .ignore_suspend = 1, ++ .ops = &mrfld_8958_ops, ++ .playback_count = 3, ++ }, ++ [MERR_SALTBAY_COMPR] = { ++ .name = "Merrifield Compress Port", ++ .stream_name = "Compress", ++ .platform_name = "sst-platform", ++ .cpu_dai_name = "Compress-cpu-dai", ++ .codec_dai_name = "wm8994-aif1", ++ .codec_name = "wm8994-codec", ++ .compr_ops = &mrfld_compr_ops, ++ }, ++ [MERR_SALTBAY_VOIP] = { ++ .name = "Merrifield VOIP Port", ++ .stream_name = "Voip", ++ .cpu_dai_name = "Voip-cpu-dai", ++ .codec_dai_name = "wm8994-aif1", ++ .codec_name = "wm8994-codec", ++ .platform_name = "sst-platform", ++ .init = NULL, ++ .ignore_suspend = 1, ++ .ops = &mrfld_8958_8k_16k_ops, ++ }, ++ [MERR_SALTBAY_PROBE] = { ++ .name = "Merrifield Probe Port", ++ .stream_name = "Probe", ++ .cpu_dai_name = "Probe-cpu-dai", ++ .codec_dai_name = "snd-soc-dummy-dai", ++ .codec_name = "snd-soc-dummy", ++ .platform_name = "sst-platform", ++ .playback_count = 8, ++ .capture_count = 8, ++ }, ++ [MERR_SALTBAY_AWARE] = { ++ .name = "Merrifield Aware Port", ++ .stream_name = "Aware", ++ .cpu_dai_name = "Loopback-cpu-dai", ++ .codec_dai_name = "wm8994-aif1", ++ .codec_name = "wm8994-codec", ++ .platform_name = "sst-platform", ++ .init = NULL, ++ .ignore_suspend = 1, ++ .ops = &mrfld_8958_8k_16k_ops, ++ }, ++ [MERR_SALTBAY_VAD] = { ++ .name = "Merrifield VAD Port", ++ .stream_name = "Vad", ++ .cpu_dai_name = "Loopback-cpu-dai", ++ .codec_dai_name = "wm8994-aif1", ++ .codec_name = "wm8994-codec", ++ .platform_name = "sst-platform", ++ .init = NULL, ++ .ignore_suspend = 1, ++ .ops = &mrfld_8958_8k_16k_ops, ++ }, ++ [MERR_SALTBAY_POWER] = { ++ .name = "Virtual Power Port", ++ .stream_name = "Power", ++ .cpu_dai_name = "Power-cpu-dai", ++ .platform_name = "sst-platform", ++ .codec_dai_name = "snd-soc-dummy-dai", ++ .codec_name = "snd-soc-dummy", ++ }, ++}; ++ ++#ifdef CONFIG_PM_SLEEP ++static int snd_mrfld_8958_prepare(struct device *dev) ++{ ++ struct snd_soc_card *card = dev_get_drvdata(dev); ++ struct snd_soc_codec *codec; ++ struct snd_soc_dapm_context *dapm; ++ ++ pr_debug("In %s\n", __func__); ++ ++ codec = mrfld_8958_get_codec(card); ++ if (!codec) { ++ pr_err("%s: couldn't find the codec pointer!\n", __func__); ++ return -EAGAIN; ++ } ++ ++ pr_debug("found codec %s\n", codec->name); ++ dapm = &codec->dapm; ++ ++ snd_soc_dapm_disable_pin(dapm, "VMID"); ++ snd_soc_dapm_sync(dapm); ++ ++ snd_soc_suspend(dev); ++ return 0; ++} ++ ++static void snd_mrfld_8958_complete(struct device *dev) ++{ ++ struct snd_soc_card *card = dev_get_drvdata(dev); ++ struct snd_soc_codec *codec; ++ struct snd_soc_dapm_context *dapm; ++ ++ pr_debug("In %s\n", __func__); ++ ++ codec = mrfld_8958_get_codec(card); ++ if (!codec) { ++ pr_err("%s: couldn't find the codec pointer!\n", __func__); ++ return; ++ } ++ ++ pr_debug("found codec %s\n", codec->name); ++ dapm = &codec->dapm; ++ ++ snd_soc_dapm_force_enable_pin(dapm, "VMID"); ++ snd_soc_dapm_sync(dapm); ++ ++ snd_soc_resume(dev); ++ return; ++} ++ ++static int snd_mrfld_8958_poweroff(struct device *dev) ++{ ++ pr_debug("In %s\n", __func__); ++ snd_soc_poweroff(dev); ++ return 0; ++} ++#else ++#define snd_mrfld_8958_prepare NULL ++#define snd_mrfld_8958_complete NULL ++#define snd_mrfld_8958_poweroff NULL ++#endif ++ ++/* SoC card */ ++static struct snd_soc_card snd_soc_card_mrfld = { ++ .name = "wm8958-audio", ++ .dai_link = mrfld_8958_msic_dailink, ++ .num_links = ARRAY_SIZE(mrfld_8958_msic_dailink), ++ .set_bias_level = mrfld_8958_set_bias_level, ++ .set_bias_level_post = mrfld_8958_set_bias_level_post, ++ .dapm_widgets = widgets, ++ .num_dapm_widgets = ARRAY_SIZE(widgets), ++ .dapm_routes = map, ++ .num_dapm_routes = ARRAY_SIZE(map), ++}; ++ ++static int snd_mrfld_8958_mc_probe(struct platform_device *pdev) ++{ ++ int ret_val = 0; ++ struct mrfld_8958_mc_private *drv; ++ ++ pr_debug("Entry %s\n", __func__); ++ ++ drv = kzalloc(sizeof(*drv), GFP_ATOMIC); ++ if (!drv) { ++ pr_err("allocation failed\n"); ++ return -ENOMEM; ++ } ++ ++ /* ioremap the register */ ++ drv->osc_clk0_reg = devm_ioremap_nocache(&pdev->dev, ++ MERR_OSC_CLKOUT_CTRL0_REG_ADDR, ++ MERR_OSC_CLKOUT_CTRL0_REG_SIZE); ++ if (!drv->osc_clk0_reg) { ++ pr_err("osc clk0 ctrl ioremap failed\n"); ++ ret_val = -1; ++ goto unalloc; ++ } ++ ++ ret_val = intel_scu_ipc_ioread8(PMIC_ID_ADDR, &drv->pmic_id); ++ if (ret_val) { ++ pr_err("Error reading PMIC ID register\n"); ++ goto unalloc; ++ } ++ ++ /* register the soc card */ ++ snd_soc_card_mrfld.dev = &pdev->dev; ++ snd_soc_card_set_drvdata(&snd_soc_card_mrfld, drv); ++ ret_val = snd_soc_register_card(&snd_soc_card_mrfld); ++ if (ret_val) { ++ pr_err("snd_soc_register_card failed %d\n", ret_val); ++ goto unalloc; ++ } ++ platform_set_drvdata(pdev, &snd_soc_card_mrfld); ++ pr_info("%s successful\n", __func__); ++ return ret_val; ++ ++unalloc: ++ kfree(drv); ++ return ret_val; ++} ++ ++static int snd_mrfld_8958_mc_remove(struct platform_device *pdev) ++{ ++ struct snd_soc_card *soc_card = platform_get_drvdata(pdev); ++ struct mrfld_8958_mc_private *drv = snd_soc_card_get_drvdata(soc_card); ++ ++ pr_debug("In %s\n", __func__); ++ kfree(drv); ++ snd_soc_card_set_drvdata(soc_card, NULL); ++ snd_soc_unregister_card(soc_card); ++ platform_set_drvdata(pdev, NULL); ++ return 0; ++} ++ ++const struct dev_pm_ops snd_mrfld_8958_mc_pm_ops = { ++ .prepare = snd_mrfld_8958_prepare, ++ .complete = snd_mrfld_8958_complete, ++ .poweroff = snd_mrfld_8958_poweroff, ++}; ++ ++static struct platform_driver snd_mrfld_8958_mc_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "mrfld_wm8958", ++ .pm = &snd_mrfld_8958_mc_pm_ops, ++ }, ++ .probe = snd_mrfld_8958_mc_probe, ++ .remove = snd_mrfld_8958_mc_remove, ++}; ++ ++static int snd_mrfld_8958_driver_init(void) ++{ ++ pr_info("Merrifield Machine Driver mrfld_wm8958 registerd\n"); ++ return platform_driver_register(&snd_mrfld_8958_mc_driver); ++} ++ ++static void snd_mrfld_8958_driver_exit(void) ++{ ++ pr_debug("In %s\n", __func__); ++ platform_driver_unregister(&snd_mrfld_8958_mc_driver); ++} ++ ++static int snd_mrfld_8958_rpmsg_probe(struct rpmsg_channel *rpdev) ++{ ++ int ret = 0; ++ ++ if (rpdev == NULL) { ++ pr_err("rpmsg channel not created\n"); ++ ret = -ENODEV; ++ goto out; ++ } ++ ++ dev_info(&rpdev->dev, "Probed snd_mrfld wm8958 rpmsg device\n"); ++ ++ ret = snd_mrfld_8958_driver_init(); ++ ++out: ++ return ret; ++} ++ ++static void snd_mrfld_8958_rpmsg_remove(struct rpmsg_channel *rpdev) ++{ ++ snd_mrfld_8958_driver_exit(); ++ dev_info(&rpdev->dev, "Removed snd_mrfld wm8958 rpmsg device\n"); ++} ++ ++static void snd_mrfld_8958_rpmsg_cb(struct rpmsg_channel *rpdev, void *data, ++ int len, void *priv, u32 src) ++{ ++ dev_warn(&rpdev->dev, "unexpected, message\n"); ++ ++ print_hex_dump(KERN_DEBUG, __func__, DUMP_PREFIX_NONE, 16, 1, ++ data, len, true); ++} ++ ++static struct rpmsg_device_id snd_mrfld_8958_rpmsg_id_table[] = { ++ { .name = "rpmsg_mrfld_wm8958_audio" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(rpmsg, snd_mrfld_8958_rpmsg_id_table); ++ ++static struct rpmsg_driver snd_mrfld_8958_rpmsg = { ++ .drv.name = KBUILD_MODNAME, ++ .drv.owner = THIS_MODULE, ++ .id_table = snd_mrfld_8958_rpmsg_id_table, ++ .probe = snd_mrfld_8958_rpmsg_probe, ++ .callback = snd_mrfld_8958_rpmsg_cb, ++ .remove = snd_mrfld_8958_rpmsg_remove, ++}; ++ ++static int __init snd_mrfld_8958_rpmsg_init(void) ++{ ++ return register_rpmsg_driver(&snd_mrfld_8958_rpmsg); ++} ++late_initcall(snd_mrfld_8958_rpmsg_init); ++ ++static void __exit snd_mrfld_8958_rpmsg_exit(void) ++{ ++ return unregister_rpmsg_driver(&snd_mrfld_8958_rpmsg); ++} ++module_exit(snd_mrfld_8958_rpmsg_exit); ++ ++MODULE_DESCRIPTION("ASoC Intel(R) Merrifield MID Machine driver"); ++MODULE_AUTHOR("Vinod Koul "); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:mrfld_wm8958"); +diff --git a/sound/soc/intel/compress.c b/sound/soc/intel/compress.c +new file mode 100644 +index 0000000..f067468 +--- /dev/null ++++ b/sound/soc/intel/compress.c +@@ -0,0 +1,254 @@ ++/* ++ * compress.c - Intel MID Platform driver for Compress stream operations ++ * ++ * Copyright (C) 2010-2013 Intel Corp ++ * Author: Vinod Koul ++ * Author: Harsha Priya ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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 ++ ++#include ++#include ++#include ++#include ++#include "platform_ipc_v2.h" ++#include "sst_platform.h" ++#include "sst_platform_pvt.h" ++ ++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; ++ struct snd_soc_pcm_runtime *rtd = cstream->private_data; ++ struct snd_soc_dai_link *dai_link = rtd->dai_link; ++ ++ pr_debug("%s called:%s\n", __func__, dai_link->cpu_dai_name); ++ ++ stream = kzalloc(sizeof(*stream), GFP_KERNEL); ++ if (!stream) ++ return -ENOMEM; ++ ++ spin_lock_init(&stream->status_lock); ++ ++ /* get the sst ops */ ++ if (!sst_dsp || !try_module_get(sst_dsp->dev->driver->owner)) { ++ pr_err("no device available to run\n"); ++ ret_val = -ENODEV; ++ goto out_ops; ++ } ++ stream->compr_ops = sst_dsp->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; ++ struct snd_soc_pcm_runtime *rtd = cstream->private_data; ++ struct snd_soc_dai_link *dai_link = rtd->dai_link; ++ 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_dsp->dev->driver->owner); ++ kfree(stream); ++ pr_debug("%s called for dai %s: ret = %d\n", __func__, ++ dai_link->cpu_dai_name, 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 = 0; ++ struct snd_sst_params str_params; ++ struct sst_compress_cb cb; ++ struct snd_soc_pcm_runtime *rtd = cstream->private_data; ++ struct snd_soc_platform *platform = rtd->platform; ++ struct sst_data *ctx = snd_soc_platform_get_drvdata(platform); ++ ++ pr_debug("In function %s\n", __func__); ++ stream = cstream->runtime->private_data; ++ /* construct fw structure for this*/ ++ memset(&str_params, 0, sizeof(str_params)); ++ ++ /* fill the device type and stream id to pass to SST driver */ ++ retval = sst_fill_stream_params(cstream, ctx, &str_params, true); ++ pr_debug("compr_set_params: fill stream params ret_val = 0x%x\n", retval); ++ if (retval < 0) ++ return retval; ++ ++ 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, ++}; +diff --git a/sound/soc/intel/effects.c b/sound/soc/intel/effects.c +new file mode 100644 +index 0000000..6bb4199 +--- /dev/null ++++ b/sound/soc/intel/effects.c +@@ -0,0 +1,405 @@ ++/* ++ * effects.c - platform file for effects interface ++ * ++ * Copyright (C) 2013 Intel Corporation ++ * Authors: Samreen Nilofer ++ * Vinod Koul ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ++ * ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ */ ++#include ++#include ++#include "platform_ipc_v2.h" ++#include "sst_platform.h" ++#include "sst_platform_pvt.h" ++ ++extern struct sst_device *sst_dsp; ++extern struct device *sst_pdev; ++ ++struct effect_uuid { ++ uint32_t timeLow; ++ uint16_t timeMid; ++ uint16_t timeHiAndVersion; ++ uint16_t clockSeq; ++ uint8_t node[6]; ++}; ++ ++#define EFFECT_STRING_LEN_MAX 64 ++ ++enum sst_effect { ++ EFFECTS_CREATE = 0, ++ EFFECTS_DESTROY, ++ EFFECTS_SET_PARAMS, ++ EFFECTS_GET_PARAMS, ++}; ++ ++enum sst_mixer_output_mode { ++ SST_MEDIA0_OUT, ++ SST_MEDIA1_OUT, ++}; ++ ++static inline void sst_fill_byte_stream(struct snd_sst_bytes_v2 *bytes, u8 type, ++ u8 msg, u8 block, u8 task, u8 pipe_id, u16 len, ++ struct ipc_effect_payload *payload) ++{ ++ u32 size = sizeof(struct ipc_effect_dsp_hdr); ++ ++ bytes->type = type; ++ bytes->ipc_msg = msg; ++ bytes->block = block; ++ bytes->task_id = task; ++ bytes->pipe_id = pipe_id; ++ bytes->len = len; ++ ++ /* Copy the ipc_effect_dsp_hdr followed by the data */ ++ memcpy(bytes->bytes, payload, size); ++ memcpy(bytes->bytes + size, payload->data, len - size); ++} ++ ++static int sst_send_effects(struct ipc_effect_payload *dsp_payload, int data_len, ++ enum sst_effect effect_type) ++{ ++ struct snd_sst_bytes_v2 *bytes; ++ u32 len; ++ int ret; ++ u8 type, msg = IPC_INVALID, pipe, payload_len; ++ struct sst_data *sst; ++ ++ if (!sst_pdev) ++ return -ENODEV; ++ sst = dev_get_drvdata(sst_pdev); ++ ++ len = sizeof(*bytes) + sizeof(struct ipc_effect_dsp_hdr) + data_len; ++ ++ bytes = kzalloc(len, GFP_KERNEL); ++ if (!bytes) { ++ pr_err("kzalloc failed allocate bytes\n"); ++ return -ENOMEM; ++ } ++ ++ switch (effect_type) { ++ case EFFECTS_CREATE: ++ case EFFECTS_DESTROY: ++ type = SND_SST_BYTES_SET; ++ msg = IPC_CMD; ++ break; ++ ++ case EFFECTS_SET_PARAMS: ++ type = SND_SST_BYTES_SET; ++ msg = IPC_SET_PARAMS; ++ break; ++ ++ case EFFECTS_GET_PARAMS: ++ type = SND_SST_BYTES_GET; ++ msg = IPC_GET_PARAMS; ++ break; ++ default: ++ pr_err("No such effect %#x", effect_type); ++ ret = -EINVAL; ++ goto free_bytes; ++ } ++ ++ pipe = dsp_payload->dsp_hdr.pipe_id; ++ payload_len = sizeof(struct ipc_effect_dsp_hdr) + data_len; ++ sst_fill_byte_stream(bytes, type, msg, 1, SST_TASK_ID_MEDIA, ++ pipe, payload_len, dsp_payload); ++ ++ mutex_lock(&sst->lock); ++ ret = sst_dsp->ops->set_generic_params(SST_SET_BYTE_STREAM, bytes); ++ mutex_unlock(&sst->lock); ++ ++ if (ret) { ++ pr_err("byte_stream failed err %d pipe_id %#x\n", ret, ++ dsp_payload->dsp_hdr.pipe_id); ++ goto free_bytes; ++ } ++ ++ /* Copy only the data - skip the dsp header */ ++ if (msg == IPC_GET_PARAMS) ++ memcpy(dsp_payload->data, bytes->bytes, data_len); ++ ++free_bytes: ++ kfree(bytes); ++ return ret; ++} ++ ++static int sst_get_algo_id(const struct sst_dev_effects *pdev_effs, ++ char *uuid, u16 *algo_id) ++{ ++ int i, len; ++ ++ len = pdev_effs->effs_num_map; ++ ++ for (i = 0; i < len; i++) { ++ if (!strncmp(pdev_effs->effs_map[i].uuid, uuid, sizeof(struct effect_uuid))) { ++ *algo_id = pdev_effs->effs_map[i].algo_id; ++ return 0; ++ } ++ } ++ pr_err("no such uuid\n"); ++ return -EINVAL; ++} ++ ++static int sst_fill_effects_info(const struct sst_dev_effects *pdev_effs, ++ char *uuid, u16 pos, ++ struct ipc_dsp_effects_info *effs_info, u16 cmd_id) ++{ ++ int i, len; ++ ++ len = pdev_effs->effs_num_map; ++ ++ for (i = 0; i < len; i++) { ++ if (!strncmp(pdev_effs->effs_map[i].uuid, uuid, sizeof(struct effect_uuid))) { ++ ++ effs_info->cmd_id = cmd_id; ++ effs_info->length = (sizeof(struct ipc_dsp_effects_info) - ++ offsetof(struct ipc_dsp_effects_info, sel_pos)); ++ effs_info->sel_pos = pos; ++ effs_info->sel_algo_id = pdev_effs->effs_map[i].algo_id; ++ effs_info->cpu_load = pdev_effs->effs_res_map[i].cpuLoad; ++ effs_info->memory_usage = pdev_effs->effs_res_map[i].memoryUsage; ++ effs_info->flags = pdev_effs->effs_res_map[i].flags; ++ ++ return 0; ++ } ++ } ++ ++ pr_err("no such uuid\n"); ++ return -EINVAL; ++} ++ ++static inline void sst_fill_dsp_payload(struct ipc_effect_payload *dsp_payload, ++ u8 pipe_id, u16 mod_id, char *data) ++{ ++ dsp_payload->dsp_hdr.mod_index_id = 0xFF; ++ dsp_payload->dsp_hdr.pipe_id = pipe_id; ++ dsp_payload->dsp_hdr.mod_id = mod_id; ++ dsp_payload->data = data; ++} ++ ++static int sst_get_pipe_id(struct sst_dev_stream_map *map, int map_size, ++ int dev, int mode, u8 *pipe_id) ++{ ++ int index; ++ ++ if (map == NULL) ++ return -EINVAL; ++ ++ /* In case of global effects, dev will be 0xff */ ++ if (dev == 0xFF) { ++ *pipe_id = (mode == SST_MEDIA0_OUT) ? PIPE_MEDIA0_OUT : PIPE_MEDIA1_OUT; ++ return 0; ++ } ++ ++ for (index = 1; index < map_size; index++) { ++ if (map[index].dev_num == dev) { ++ *pipe_id = map[index].device_id; ++ break; ++ } ++ } ++ ++ if (index == map_size) { ++ pr_err("no such device %d\n", dev); ++ return -ENODEV; ++ } ++ return 0; ++} ++ ++static int sst_effects_create(struct snd_card *card, struct snd_effect *effect) ++{ ++ int ret = 0; ++ u8 pipe_id; ++ struct ipc_effect_payload dsp_payload; ++ struct ipc_dsp_effects_info effects_info; ++ struct sst_data *sst; ++ ++ if (!sst_pdev) ++ return -ENODEV; ++ sst = dev_get_drvdata(sst_pdev); ++ ++ ret = sst_fill_effects_info(&sst->pdata->pdev_effs, effect->uuid, effect->pos, ++ &effects_info, IPC_EFFECTS_CREATE); ++ if (ret < 0) ++ return ret; ++ ++ ret = sst_get_pipe_id(sst->pdata->pdev_strm_map, ++ sst->pdata->strm_map_size, ++ effect->device, effect->mode, &pipe_id); ++ if (ret < 0) ++ return ret; ++ ++ sst_fill_dsp_payload(&dsp_payload, pipe_id, 0xFF, (char *)&effects_info); ++ ++ ret = sst_send_effects(&dsp_payload, sizeof(effects_info), EFFECTS_CREATE); ++ ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int sst_effects_destroy(struct snd_card *card, struct snd_effect *effect) ++{ ++ int ret = 0; ++ u8 pipe_id; ++ struct ipc_effect_payload dsp_payload; ++ struct ipc_dsp_effects_info effects_info; ++ struct sst_data *sst; ++ ++ if (!sst_pdev) ++ return -ENODEV; ++ sst = dev_get_drvdata(sst_pdev); ++ ++ ret = sst_fill_effects_info(&sst->pdata->pdev_effs, effect->uuid, effect->pos, ++ &effects_info, IPC_EFFECTS_DESTROY); ++ if (ret < 0) ++ return ret; ++ ++ ret = sst_get_pipe_id(sst->pdata->pdev_strm_map, ++ sst->pdata->strm_map_size, ++ effect->device, effect->mode, &pipe_id); ++ if (ret < 0) ++ return ret; ++ ++ sst_fill_dsp_payload(&dsp_payload, pipe_id, 0xFF, (char *)&effects_info); ++ ++ ret = sst_send_effects(&dsp_payload, sizeof(effects_info), EFFECTS_DESTROY); ++ ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int sst_effects_set_params(struct snd_card *card, ++ struct snd_effect_params *params) ++{ ++ int ret = 0; ++ u8 pipe_id; ++ u16 algo_id; ++ struct ipc_effect_payload dsp_payload; ++ struct sst_data *sst; ++ ++ if (!sst_pdev) ++ return -ENODEV; ++ sst = dev_get_drvdata(sst_pdev); ++ ++ ret = sst_get_algo_id(&sst->pdata->pdev_effs, params->uuid, &algo_id); ++ if (ret < 0) ++ return ret; ++ ++ ret = sst_get_pipe_id(sst->pdata->pdev_strm_map, ++ sst->pdata->strm_map_size, ++ params->device, SST_MEDIA0_OUT, &pipe_id); ++ if (ret < 0) ++ return ret; ++ ++ sst_fill_dsp_payload(&dsp_payload, pipe_id, algo_id, params->buffer); ++ ++ ret = sst_send_effects(&dsp_payload, params->size, EFFECTS_SET_PARAMS); ++ ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int sst_effects_get_params(struct snd_card *card, ++ struct snd_effect_params *params) ++{ ++ int ret = 0; ++ u8 pipe_id; ++ u16 algo_id; ++ struct ipc_effect_payload dsp_payload; ++ struct sst_data *sst; ++ ++ if (!sst_pdev) ++ return -ENODEV; ++ sst = dev_get_drvdata(sst_pdev); ++ ++ ret = sst_get_algo_id(&sst->pdata->pdev_effs, params->uuid, &algo_id); ++ if (ret < 0) ++ return ret; ++ ++ ret = sst_get_pipe_id(sst->pdata->pdev_strm_map, ++ sst->pdata->strm_map_size, ++ params->device, SST_MEDIA0_OUT, &pipe_id); ++ if (ret < 0) ++ return ret; ++ ++ sst_fill_dsp_payload(&dsp_payload, pipe_id, algo_id, params->buffer); ++ ++ ret = sst_send_effects(&dsp_payload, params->size, EFFECTS_GET_PARAMS); ++ ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int sst_query_num_effects(struct snd_card *card) ++{ ++ struct sst_data *sst; ++ ++ if (!sst_pdev) ++ return -ENODEV; ++ sst = dev_get_drvdata(sst_pdev); ++ ++ return sst->pdata->pdev_effs.effs_num_map; ++} ++ ++static int sst_query_effects_caps(struct snd_card *card, ++ struct snd_effect_caps *caps) ++{ ++ struct sst_data *sst; ++ struct sst_dev_effects_map *effs_map; ++ unsigned int num_effects, offset = 0; ++ char *dstn; ++ int i; ++ ++ if (!sst_pdev) ++ return -ENODEV; ++ sst = dev_get_drvdata(sst_pdev); ++ ++ effs_map = sst->pdata->pdev_effs.effs_map; ++ num_effects = sst->pdata->pdev_effs.effs_num_map; ++ ++ if (caps->size < (num_effects * MAX_DESCRIPTOR_SIZE)) { ++ pr_err("buffer size is insufficient\n"); ++ return -ENOMEM; ++ } ++ ++ dstn = caps->buffer; ++ for (i = 0; i < num_effects; i++) { ++ memcpy(dstn + offset, effs_map[i].descriptor, MAX_DESCRIPTOR_SIZE); ++ offset += MAX_DESCRIPTOR_SIZE; ++ } ++ caps->size = offset; ++ ++ return 0; ++} ++ ++struct snd_effect_ops effects_ops = { ++ .create = sst_effects_create, ++ .destroy = sst_effects_destroy, ++ .set_params = sst_effects_set_params, ++ .get_params = sst_effects_get_params, ++ .query_num_effects = sst_query_num_effects, ++ .query_effect_caps = sst_query_effects_caps, ++}; +diff --git a/sound/soc/intel/pcm.c b/sound/soc/intel/pcm.c +new file mode 100644 +index 0000000..8600147 +--- /dev/null ++++ b/sound/soc/intel/pcm.c +@@ -0,0 +1,1021 @@ ++/* ++ * pcm.c - Intel MID Platform driver file implementing PCM functionality ++ * ++ * Copyright (C) 2010-2013 Intel Corp ++ * Author: Vinod Koul ++ * Author: Harsha Priya ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "platform_ipc_v2.h" ++#include "sst_platform.h" ++#include "sst_platform_pvt.h" ++ ++struct device *sst_pdev; ++struct sst_device *sst_dsp; ++extern struct snd_compr_ops sst_platform_compr_ops; ++extern struct snd_effect_ops effects_ops; ++ ++static DEFINE_MUTEX(sst_dsp_lock); ++ ++static struct snd_pcm_hardware sst_platform_pcm_hw = { ++ .info = (SNDRV_PCM_INFO_INTERLEAVED | ++ SNDRV_PCM_INFO_DOUBLE | ++ SNDRV_PCM_INFO_PAUSE | ++ SNDRV_PCM_INFO_RESUME | ++ SNDRV_PCM_INFO_MMAP| ++ SNDRV_PCM_INFO_MMAP_VALID | ++ SNDRV_PCM_INFO_BLOCK_TRANSFER | ++ SNDRV_PCM_INFO_SYNC_START), ++ .formats = (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16 | ++ SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_U24 | ++ SNDRV_PCM_FMTBIT_S32 | SNDRV_PCM_FMTBIT_U32), ++ .rates = (SNDRV_PCM_RATE_8000| ++ SNDRV_PCM_RATE_16000 | ++ SNDRV_PCM_RATE_44100 | ++ SNDRV_PCM_RATE_48000), ++ .rate_min = SST_MIN_RATE, ++ .rate_max = SST_MAX_RATE, ++ .channels_min = SST_MIN_CHANNEL, ++ .channels_max = SST_MAX_CHANNEL, ++ .buffer_bytes_max = SST_MAX_BUFFER, ++ .period_bytes_min = SST_MIN_PERIOD_BYTES, ++ .period_bytes_max = SST_MAX_PERIOD_BYTES, ++ .periods_min = SST_MIN_PERIODS, ++ .periods_max = SST_MAX_PERIODS, ++ .fifo_size = SST_FIFO_SIZE, ++}; ++ ++static int sst_platform_ihf_set_tdm_slot(struct snd_soc_dai *dai, ++ unsigned int tx_mask, unsigned int rx_mask, ++ int slots, int slot_width) { ++ struct snd_sst_runtime_params params_data; ++ int channels = slots; ++ ++ /* registering with SST driver to get access to SST APIs to use */ ++ if (!sst_dsp) { ++ pr_err("sst: DSP not registered\n"); ++ return -EIO; ++ } ++ params_data.type = SST_SET_CHANNEL_INFO; ++ params_data.str_id = SND_SST_DEVICE_IHF; ++ params_data.size = sizeof(channels); ++ params_data.addr = &channels; ++ return sst_dsp->ops->set_generic_params(SST_SET_RUNTIME_PARAMS, ++ (void *)¶ms_data); ++} ++ ++static int sst_media_digital_mute(struct snd_soc_dai *dai, int mute, int stream) ++{ ++ ++ pr_debug("%s: enter, mute=%d dai-name=%s dir=%d\n", __func__, mute, dai->name, stream); ++ ++#if IS_BUILTIN(CONFIG_SST_MRFLD_DPCM) ++ sst_send_pipe_gains(dai, stream, mute); ++#endif ++ ++ return 0; ++} ++ ++/* helper functions */ ++void sst_set_stream_status(struct sst_runtime_stream *stream, ++ int state) ++{ ++ unsigned long flags; ++ spin_lock_irqsave(&stream->status_lock, flags); ++ stream->stream_status = state; ++ spin_unlock_irqrestore(&stream->status_lock, flags); ++} ++ ++static inline int sst_get_stream_status(struct sst_runtime_stream *stream) ++{ ++ int state; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&stream->status_lock, flags); ++ state = stream->stream_status; ++ spin_unlock_irqrestore(&stream->status_lock, flags); ++ return state; ++} ++ ++static void sst_fill_alloc_params(struct snd_pcm_substream *substream, ++ struct snd_sst_alloc_params_ext *alloc_param) ++{ ++ unsigned int channels; ++ snd_pcm_uframes_t period_size; ++ ssize_t periodbytes; ++ ssize_t buffer_bytes = snd_pcm_lib_buffer_bytes(substream); ++ u32 buffer_addr = virt_to_phys(substream->dma_buffer.area); ++ ++ channels = substream->runtime->channels; ++ period_size = substream->runtime->period_size; ++ periodbytes = samples_to_bytes(substream->runtime, period_size); ++ alloc_param->ring_buf_info[0].addr = buffer_addr; ++ alloc_param->ring_buf_info[0].size = buffer_bytes; ++ alloc_param->sg_count = 1; ++ alloc_param->reserved = 0; ++ alloc_param->frag_size = periodbytes * channels; ++ ++ pr_debug("period_size = %d\n", alloc_param->frag_size); ++ pr_debug("ring_buf_addr = 0x%x\n", alloc_param->ring_buf_info[0].addr); ++} ++static void sst_fill_pcm_params(struct snd_pcm_substream *substream, ++ struct snd_sst_stream_params *param) ++{ ++ param->uc.pcm_params.num_chan = (u8) substream->runtime->channels; ++ param->uc.pcm_params.pcm_wd_sz = substream->runtime->sample_bits; ++ param->uc.pcm_params.sfreq = substream->runtime->rate; ++ ++ /* PCM stream via ALSA interface */ ++ param->uc.pcm_params.use_offload_path = 0; ++ param->uc.pcm_params.reserved2 = 0; ++ memset(param->uc.pcm_params.channel_map, 0, sizeof(u8)); ++ pr_debug("sfreq= %d, wd_sz = %d\n", ++ param->uc.pcm_params.sfreq, param->uc.pcm_params.pcm_wd_sz); ++ ++} ++ ++#define ASSIGN_PIPE_ID(periodtime, lowlatency, deepbuffer) \ ++ ((periodtime) <= (lowlatency) ? PIPE_LOW_PCM0_IN : \ ++ ((periodtime) >= (deepbuffer) ? PIPE_MEDIA3_IN : PIPE_MEDIA1_IN)) ++ ++static int sst_get_stream_mapping(int dev, int sdev, int dir, ++ struct sst_dev_stream_map *map, int size, u8 pipe_id, ++ const struct sst_lowlatency_deepbuff *ll_db) ++{ ++ int index; ++ unsigned long pt = 0, ll = 0, db = 0; ++ ++ if (map == NULL) ++ return -EINVAL; ++ ++ pr_debug("dev %d sdev %d dir %d\n", dev, sdev, dir); ++ ++ /* index 0 is not used in stream map */ ++ for (index = 1; index < size; index++) { ++ if ((map[index].dev_num == dev) && ++ (map[index].subdev_num == sdev) && ++ (map[index].direction == dir)) { ++ /* device id for the probe is assigned dynamically */ ++ if (map[index].status == SST_DEV_MAP_IN_USE) { ++ return index; ++ } else if (map[index].status == SST_DEV_MAP_FREE) { ++ map[index].status = SST_DEV_MAP_IN_USE; ++ ++ if (map[index].dev_num == MERR_SALTBAY_PROBE) { ++ map[index].device_id = pipe_id; ++ ++ } else if (map[index].dev_num == MERR_SALTBAY_AUDIO) { ++ if (!ll_db->low_latency || !ll_db->deep_buffer) ++ return -EINVAL; ++ ++ pt = ll_db->period_time; ++ ll = *(ll_db->low_latency); ++ db = *(ll_db->deep_buffer); ++ ++ pr_debug("PT %lu LL %lu DB %lu\n", pt, ll, db); ++ ++ map[index].device_id = ASSIGN_PIPE_ID(pt, ++ ll, db); ++ } ++ pr_debug("%s: pipe_id 0%x index %d", __func__, ++ map[index].device_id, index); ++ ++ return index; ++ } ++ } ++ } ++ return 0; ++} ++ ++int sst_fill_stream_params(void *substream, ++ const struct sst_data *ctx, struct snd_sst_params *str_params, bool is_compress) ++{ ++ int map_size; ++ int index; ++ struct sst_dev_stream_map *map; ++ struct snd_pcm_substream *pstream = NULL; ++ struct snd_compr_stream *cstream = NULL; ++ ++ map = ctx->pdata->pdev_strm_map; ++ map_size = ctx->pdata->strm_map_size; ++ ++ if (is_compress == true) ++ cstream = (struct snd_compr_stream *)substream; ++ else ++ pstream = (struct snd_pcm_substream *)substream; ++ ++ str_params->stream_type = SST_STREAM_TYPE_MUSIC; ++ ++ /* For pcm streams */ ++ if (pstream) { ++ index = sst_get_stream_mapping(pstream->pcm->device, ++ pstream->number, pstream->stream, ++ map, map_size, ctx->pipe_id, &ctx->ll_db); ++ if (index <= 0) ++ return -EINVAL; ++ ++ str_params->stream_id = index; ++ str_params->device_type = map[index].device_id; ++ str_params->task = map[index].task_id; ++ ++ if (str_params->device_type == SST_PROBE_IN) ++ str_params->stream_type = SST_STREAM_TYPE_PROBE; ++ ++ pr_debug("str_id = %d, device_type = 0x%x, task = %d", ++ str_params->stream_id, str_params->device_type, ++ str_params->task); ++ ++ str_params->ops = (u8)pstream->stream; ++ } ++ ++ if (cstream) { ++ /* FIXME: Add support for subdevice number in ++ * snd_compr_stream */ ++ index = sst_get_stream_mapping(cstream->device->device, ++ 0, cstream->direction, ++ map, map_size, ctx->pipe_id, &ctx->ll_db); ++ if (index <= 0) ++ return -EINVAL; ++ str_params->stream_id = index; ++ str_params->device_type = map[index].device_id; ++ str_params->task = map[index].task_id; ++ pr_debug("compress str_id = %d, device_type = 0x%x, task = %d", ++ str_params->stream_id, str_params->device_type, ++ str_params->task); ++ ++ str_params->ops = (u8)cstream->direction; ++ } ++ return 0; ++} ++ ++#define CALC_PERIODTIME(period_size, rate) (((period_size) * 1000) / (rate)) ++ ++static int sst_platform_alloc_stream(struct snd_pcm_substream *substream, ++ struct snd_soc_platform *platform) ++{ ++ struct sst_runtime_stream *stream = ++ substream->runtime->private_data; ++ struct snd_sst_stream_params param = {{{0,},},}; ++ struct snd_sst_params str_params = {0}; ++ struct snd_sst_alloc_params_ext alloc_params = {0}; ++ int ret_val = 0; ++ struct sst_data *ctx = snd_soc_platform_get_drvdata(platform); ++ ++ /* set codec params and inform SST driver the same */ ++ sst_fill_pcm_params(substream, ¶m); ++ sst_fill_alloc_params(substream, &alloc_params); ++ substream->runtime->dma_area = substream->dma_buffer.area; ++ str_params.sparams = param; ++ str_params.aparams = alloc_params; ++ str_params.codec = SST_CODEC_TYPE_PCM; ++ ++ ctx->ll_db.period_time = CALC_PERIODTIME(substream->runtime->period_size, ++ substream->runtime->rate); ++ ++ /* fill the device type and stream id to pass to SST driver */ ++ ret_val = sst_fill_stream_params(substream, ctx, &str_params, false); ++ pr_debug("platform prepare: fill stream params ret_val = 0x%x\n", ret_val); ++ if (ret_val < 0) ++ return ret_val; ++ ++ stream->stream_info.str_id = str_params.stream_id; ++ ++ ret_val = stream->ops->open(&str_params); ++ pr_debug("platform prepare: stream open ret_val = 0x%x\n", ret_val); ++ if (ret_val <= 0) ++ return ret_val; ++ ++ pr_debug("platform allocated strid: %d\n", stream->stream_info.str_id); ++ ++ return ret_val; ++} ++ ++static void sst_period_elapsed(void *mad_substream) ++{ ++ struct snd_pcm_substream *substream = mad_substream; ++ struct sst_runtime_stream *stream; ++ int status; ++ ++ if (!substream || !substream->runtime) { ++ pr_debug("In %s : Null Substream pointer\n", __func__); ++ return; ++ } ++ stream = substream->runtime->private_data; ++ if (!stream) { ++ pr_debug("In %s : Null Stream pointer\n", __func__); ++ return; ++ } ++ status = sst_get_stream_status(stream); ++ if (status != SST_PLATFORM_RUNNING) { ++ pr_debug("In %s : Stream Status=%d\n", __func__, status); ++ return; ++ } ++ snd_pcm_period_elapsed(substream); ++} ++ ++static int sst_platform_init_stream(struct snd_pcm_substream *substream) ++{ ++ struct sst_runtime_stream *stream = ++ substream->runtime->private_data; ++ int ret_val; ++ ++ pr_debug("setting buffer ptr param\n"); ++ sst_set_stream_status(stream, SST_PLATFORM_INIT); ++ stream->stream_info.period_elapsed = sst_period_elapsed; ++ stream->stream_info.mad_substream = substream; ++ stream->stream_info.buffer_ptr = 0; ++ stream->stream_info.sfreq = substream->runtime->rate; ++ pr_debug("pcm_substream %p, period_elapsed %p\n", ++ stream->stream_info.mad_substream, stream->stream_info.period_elapsed); ++ ret_val = stream->ops->device_control( ++ SST_SND_STREAM_INIT, &stream->stream_info); ++ if (ret_val) ++ pr_err("control_set ret error %d\n", ret_val); ++ return ret_val; ++ ++} ++ ++static inline int power_up_sst(struct sst_runtime_stream *sst) ++{ ++ return sst->ops->power(true); ++} ++ ++static inline int power_down_sst(struct sst_runtime_stream *sst) ++{ ++ return sst->ops->power(false); ++} ++/* end -- helper functions */ ++ ++static int sst_media_open(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ int ret_val = 0; ++ struct snd_pcm_runtime *runtime = substream->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 */ ++ mutex_lock(&sst_dsp_lock); ++ if (!sst_dsp || ++ !try_module_get(sst_dsp->dev->driver->owner)) { ++ pr_err("no device available to run\n"); ++ ret_val = -ENODEV; ++ goto out_ops; ++ } ++ stream->ops = sst_dsp->ops; ++ mutex_unlock(&sst_dsp_lock); ++ ++ stream->stream_info.str_id = 0; ++ sst_set_stream_status(stream, SST_PLATFORM_UNINIT); ++ stream->stream_info.mad_substream = substream; ++ runtime->private_data = stream; ++ ++ if (strstr(dai->name, "Power-cpu-dai")) ++ return power_up_sst(stream); ++ ++ /* Make sure, that the period size is always even */ ++ snd_pcm_hw_constraint_step(substream->runtime, 0, ++ SNDRV_PCM_HW_PARAM_PERIODS, 2); ++ ++ pr_debug("buf_ptr %llu\n", stream->stream_info.buffer_ptr); ++ return snd_pcm_hw_constraint_integer(runtime, ++ SNDRV_PCM_HW_PARAM_PERIODS); ++out_ops: ++ kfree(stream); ++ mutex_unlock(&sst_dsp_lock); ++ return ret_val; ++} ++ ++static void sst_free_stream_in_use(struct sst_dev_stream_map *map, int str_id) ++{ ++#if IS_BUILTIN(CONFIG_SST_MRFLD_DPCM) ++ return; ++#else ++ if ((map[str_id].dev_num == MERR_SALTBAY_AUDIO) || ++ (map[str_id].dev_num == MERR_SALTBAY_PROBE)) { ++ ++ /* Do nothing in capture for audio device */ ++ if ((map[str_id].dev_num == MERR_SALTBAY_AUDIO) && ++ (map[str_id].direction == SNDRV_PCM_STREAM_CAPTURE)) ++ return; ++ if ((map[str_id].task_id == SST_TASK_ID_MEDIA) && ++ (map[str_id].status == SST_DEV_MAP_IN_USE)) { ++ pr_debug("str_id %d device_id 0x%x\n", str_id, map[str_id].device_id); ++ map[str_id].status = SST_DEV_MAP_FREE; ++ map[str_id].device_id = PIPE_RSVD; ++ } ++ } ++ return; ++#endif ++} ++ ++static void sst_media_close(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct sst_runtime_stream *stream; ++ int ret_val = 0, str_id; ++ struct sst_data *ctx = snd_soc_platform_get_drvdata(dai->platform); ++ ++ stream = substream->runtime->private_data; ++ if (strstr(dai->name, "Power-cpu-dai")) ++ ret_val = power_down_sst(stream); ++ ++ str_id = stream->stream_info.str_id; ++ if (str_id) ++ ret_val = stream->ops->close(str_id); ++ sst_free_stream_in_use(ctx->pdata->pdev_strm_map, str_id); ++ module_put(sst_dsp->dev->driver->owner); ++ kfree(stream); ++ pr_debug("%s: %d\n", __func__, ret_val); ++} ++ ++static int sst_dpcm_probe_cmd(struct snd_soc_platform *platform, ++ struct snd_pcm_substream *substream, u16 pipe_id, bool on) ++{ ++ int ret = 0; ++#if IS_BUILTIN(CONFIG_SST_MRFLD_DPCM) ++ if (substream->pcm->device == MERR_DPCM_PROBE) ++ ret = sst_dpcm_probe_send(platform, pipe_id, substream->number, ++ substream->stream, on); ++#endif ++ return ret; ++} ++ ++static inline unsigned int get_current_pipe_id(struct snd_soc_platform *platform, ++ struct snd_pcm_substream *substream) ++{ ++ struct sst_data *sst = snd_soc_platform_get_drvdata(platform); ++ struct sst_dev_stream_map *map = sst->pdata->pdev_strm_map; ++ struct sst_runtime_stream *stream = ++ substream->runtime->private_data; ++ u32 str_id = stream->stream_info.str_id; ++ unsigned int pipe_id; ++ pipe_id = map[str_id].device_id; ++ ++ pr_debug("%s: got pipe_id = %#x for str_id = %d\n", ++ __func__, pipe_id, str_id); ++ return pipe_id; ++} ++ ++static void sst_probe_close(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ u16 probe_pipe_id = get_current_pipe_id(dai->platform, substream); ++ ++ sst_dpcm_probe_cmd(dai->platform, substream, probe_pipe_id, false); ++ sst_media_close(substream, dai); ++} ++ ++static int sst_media_prepare(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct sst_runtime_stream *stream; ++ int ret_val = 0, str_id; ++ ++ pr_debug("%s\n", __func__); ++ ++ stream = substream->runtime->private_data; ++ str_id = stream->stream_info.str_id; ++ if (stream->stream_info.str_id) ++ return ret_val; ++ ++ ret_val = sst_platform_alloc_stream(substream, dai->platform); ++ if (ret_val <= 0) ++ return ret_val; ++ snprintf(substream->pcm->id, sizeof(substream->pcm->id), ++ "%d", stream->stream_info.str_id); ++ ++ ret_val = sst_platform_init_stream(substream); ++ if (ret_val) ++ return ret_val; ++ substream->runtime->hw.info = SNDRV_PCM_INFO_BLOCK_TRANSFER; ++ ++ return ret_val; ++} ++ ++static int sst_probe_prepare(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ u16 probe_pipe_id; ++ ++ sst_media_prepare(substream, dai); ++ probe_pipe_id = get_current_pipe_id(dai->platform, substream); ++ ++ return sst_dpcm_probe_cmd(dai->platform, substream, probe_pipe_id, true); ++} ++ ++static int sst_media_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) ++{ ++ pr_debug("%s\n", __func__); ++ ++ snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); ++ memset(substream->runtime->dma_area, 0, params_buffer_bytes(params)); ++ return 0; ++} ++ ++static int sst_media_hw_free(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ return snd_pcm_lib_free_pages(substream); ++} ++ ++static struct snd_soc_dai_ops sst_media_dai_ops = { ++ .startup = sst_media_open, ++ .shutdown = sst_media_close, ++ .prepare = sst_media_prepare, ++ .hw_params = sst_media_hw_params, ++ .hw_free = sst_media_hw_free, ++ .set_tdm_slot = sst_platform_ihf_set_tdm_slot, ++ .mute_stream = sst_media_digital_mute, ++}; ++ ++static struct snd_soc_dai_ops sst_probe_dai_ops = { ++ .startup = sst_media_open, ++ .hw_params = sst_media_hw_params, ++ .hw_free = sst_media_hw_free, ++ .shutdown = sst_probe_close, ++ .prepare = sst_probe_prepare, ++}; ++ ++static struct snd_soc_dai_ops sst_loopback_dai_ops = { ++ .startup = sst_media_open, ++ .shutdown = sst_media_close, ++ .prepare = sst_media_prepare, ++}; ++ ++static struct snd_soc_dai_ops sst_compr_dai_ops = { ++ .mute_stream = sst_media_digital_mute, ++}; ++ ++static struct snd_soc_dai_driver sst_platform_dai[] = { ++{ ++ .name = SST_HEADSET_DAI, ++ .ops = &sst_media_dai_ops, ++ .playback = { ++ .stream_name = "Headset Playback", ++ .channels_min = SST_STEREO, ++ .channels_max = SST_STEREO, ++ .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++ .capture = { ++ .stream_name = "Headset Capture", ++ .channels_min = 1, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++}, ++{ ++ .name = SST_DEEPBUFFER_DAI, ++ .ops = &sst_media_dai_ops, ++ .playback = { ++ .stream_name = "Deepbuffer Playback", ++ .channels_min = SST_STEREO, ++ .channels_max = SST_STEREO, ++ .rates = SNDRV_PCM_RATE_48000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++}, ++{ ++ .name = SST_LOWLATENCY_DAI, ++ .ops = &sst_media_dai_ops, ++ .playback = { ++ .stream_name = "Low Latency Playback", ++ .channels_min = SST_STEREO, ++ .channels_max = SST_STEREO, ++ .rates = SNDRV_PCM_RATE_48000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++}, ++{ ++ .name = SST_SPEAKER_DAI, ++ .ops = &sst_media_dai_ops, ++ .playback = { ++ .stream_name = "Speaker Playback", ++ .channels_min = SST_MONO, ++ .channels_max = SST_STEREO, ++ .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++}, ++{ ++ .name = SST_VOICE_DAI, ++ .playback = { ++ .stream_name = "Voice Downlink", ++ .channels_min = SST_MONO, ++ .channels_max = SST_STEREO, ++ .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++ .capture = { ++ .stream_name = "Voice Uplink", ++ .channels_min = SST_MONO, ++ .channels_max = SST_STEREO, ++ .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++}, ++{ ++ .name = SST_COMPRESS_DAI, ++ .compress_dai = 1, ++ .ops = &sst_compr_dai_ops, ++ .playback = { ++ .stream_name = "Compress Playback", ++ .channels_min = SST_STEREO, ++ .channels_max = SST_STEREO, ++ .rates = SNDRV_PCM_RATE_48000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++}, ++{ ++ .name = SST_VIRTUAL_DAI, ++ .playback = { ++ .stream_name = "Virtual Playback", ++ .channels_min = SST_STEREO, ++ .channels_max = SST_STEREO, ++ .rates = SNDRV_PCM_RATE_48000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++}, ++{ ++ .name = SST_POWER_DAI, ++ .ops = &sst_media_dai_ops, ++ .playback = { ++ .stream_name = "Dummy Power Stream", ++ .channels_min = SST_MONO, ++ .channels_max = SST_STEREO, ++ .rates = SNDRV_PCM_RATE_8000_48000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, ++ }, ++}, ++{ ++ .name = SST_PROBE_DAI, ++ .ops = &sst_probe_dai_ops, ++ .playback = { ++ .stream_name = "Probe Playback", ++ .channels_min = SST_MONO, ++ .channels_max = SST_STEREO, ++ .rates = SNDRV_PCM_RATE_8000 | ++ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, ++ }, ++ .capture = { ++ .stream_name = "Probe Capture", ++ .channels_min = SST_MONO, ++ .channels_max = SST_STEREO, ++ .rates = SNDRV_PCM_RATE_8000 | ++ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, ++ }, ++}, ++{ ++ .name = SST_VOIP_DAI, ++ .ops = &sst_media_dai_ops, ++ .playback = { ++ .stream_name = "VOIP Playback", ++ .channels_min = SST_MONO, ++ .channels_max = SST_STEREO, ++ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++ .capture = { ++ .stream_name = "VOIP Capture", ++ .channels_min = SST_MONO, ++ .channels_max = SST_STEREO, ++ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++}, ++{ ++ .name = SST_LOOPBACK_DAI, ++ .ops = &sst_loopback_dai_ops, ++ .capture = { ++ .stream_name = "Loopback Capture", ++ .channels_min = SST_MONO, ++ .channels_max = SST_MONO, ++ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++}, ++/*BE CPU Dais */ ++{ ++ .name = "ssp2-codec", ++ .playback = { ++ .stream_name = "ssp2 playback", ++ .channels_min = SST_STEREO, ++ .channels_max = SST_STEREO, ++ .rates = SNDRV_PCM_RATE_48000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++ .capture = { ++ .stream_name = "ssp2 Capture", ++ .channels_min = SST_STEREO, ++ .channels_max = SST_STEREO, ++ .rates = SNDRV_PCM_RATE_48000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++}, ++}; ++ ++static int sst_platform_open(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime; ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_dai_link *dai_link = rtd->dai_link; ++ ++ pr_debug("sst_platform_open called:%s\n", dai_link->cpu_dai_name); ++ if (substream->pcm->internal) ++ return 0; ++ runtime = substream->runtime; ++ runtime->hw = sst_platform_pcm_hw; ++ return 0; ++} ++ ++static int sst_platform_close(struct snd_pcm_substream *substream) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_dai_link *dai_link = rtd->dai_link; ++ pr_debug("sst_platform_close called:%s\n", dai_link->cpu_dai_name); ++ return 0; ++} ++ ++static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream, ++ int cmd) ++{ ++ int ret_val = 0, str_id; ++ struct sst_runtime_stream *stream; ++ int str_cmd, status, alsa_state; ++ ++ if (substream->pcm->internal) ++ return 0; ++ pr_debug("sst_platform_pcm_trigger called\n"); ++ stream = substream->runtime->private_data; ++ str_id = stream->stream_info.str_id; ++ alsa_state = substream->runtime->status->state; ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ pr_debug("Trigger Start\n"); ++ str_cmd = SST_SND_START; ++ status = SST_PLATFORM_RUNNING; ++ stream->stream_info.mad_substream = substream; ++ break; ++ case SNDRV_PCM_TRIGGER_STOP: ++ pr_debug("Trigger stop\n"); ++ str_cmd = SST_SND_DROP; ++ status = SST_PLATFORM_DROPPED; ++ break; ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ pr_debug("Trigger pause\n"); ++ str_cmd = SST_SND_PAUSE; ++ status = SST_PLATFORM_PAUSED; ++ break; ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ pr_debug("Trigger pause release\n"); ++ str_cmd = SST_SND_RESUME; ++ status = SST_PLATFORM_RUNNING; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ret_val = stream->ops->device_control(str_cmd, &str_id); ++ if (!ret_val) ++ sst_set_stream_status(stream, status); ++ ++ return ret_val; ++} ++ ++ ++static snd_pcm_uframes_t sst_platform_pcm_pointer ++ (struct snd_pcm_substream *substream) ++{ ++ struct sst_runtime_stream *stream; ++ int ret_val, status; ++ struct pcm_stream_info *str_info; ++ ++ stream = substream->runtime->private_data; ++ status = sst_get_stream_status(stream); ++ if (status == SST_PLATFORM_INIT) ++ return 0; ++ str_info = &stream->stream_info; ++ ret_val = stream->ops->device_control( ++ SST_SND_BUFFER_POINTER, str_info); ++ if (ret_val) { ++ pr_err("sst: error code = %d\n", ret_val); ++ return ret_val; ++ } ++ substream->runtime->soc_delay = str_info->pcm_delay; ++ return str_info->buffer_ptr; ++} ++ ++static struct snd_pcm_ops sst_platform_ops = { ++ .open = sst_platform_open, ++ .close = sst_platform_close, ++ .ioctl = snd_pcm_lib_ioctl, ++ .trigger = sst_platform_pcm_trigger, ++ .pointer = sst_platform_pcm_pointer, ++}; ++ ++static void sst_pcm_free(struct snd_pcm *pcm) ++{ ++ pr_debug("sst_pcm_free called\n"); ++ snd_pcm_lib_preallocate_free_for_all(pcm); ++} ++ ++static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd) ++{ ++ struct snd_soc_dai *dai = rtd->cpu_dai; ++ struct snd_pcm *pcm = rtd->pcm; ++ int retval = 0; ++ ++ pr_debug("sst_pcm_new called\n"); ++ if (dai->driver->playback.channels_min || ++ dai->driver->capture.channels_min) { ++ retval = snd_pcm_lib_preallocate_pages_for_all(pcm, ++ SNDRV_DMA_TYPE_CONTINUOUS, ++ snd_dma_continuous_data(GFP_DMA), ++ SST_MAX_BUFFER, SST_MAX_BUFFER); ++ if (retval) { ++ pr_err("dma buffer allocationf fail\n"); ++ return retval; ++ } ++ } ++ return retval; ++} ++ ++static int sst_soc_probe(struct snd_soc_platform *platform) ++{ ++ struct sst_data *ctx = snd_soc_platform_get_drvdata(platform); ++ struct soft_platform_id spid; ++ int ret = 0; ++ ++ memcpy(&spid, ctx->pdata->spid, sizeof(spid)); ++ pr_err("Enter:%s\n", __func__); ++ ++#if IS_BUILTIN(CONFIG_SST_MRFLD_DPCM) ++ ret = sst_dsp_init_v2_dpcm(platform); ++#else ++ ret = sst_dsp_init(platform); ++#endif ++// if (ret) ++// return ret; ++// ret = snd_soc_register_effect(platform->card, &effects_ops); ++ ++ return ret; ++} ++ ++static int sst_soc_remove(struct snd_soc_platform *platform) ++{ ++ pr_debug("%s called\n", __func__); ++ return 0; ++} ++ ++static struct snd_soc_platform_driver sst_soc_platform_drv = { ++ .probe = sst_soc_probe, ++ .remove = sst_soc_remove, ++ .ops = &sst_platform_ops, ++ .compr_ops = &sst_platform_compr_ops, ++ .pcm_new = sst_pcm_new, ++ .pcm_free = sst_pcm_free, ++ .read = sst_soc_read, ++ .write = sst_soc_write, ++}; ++ ++int sst_register_dsp(struct sst_device *sst_dev) ++{ ++ if (!sst_dev) ++ return -ENODEV; ++ mutex_lock(&sst_dsp_lock); ++ if (sst_dsp) { ++ pr_err("we already have a device %s\n", sst_dsp->name); ++ mutex_unlock(&sst_dsp_lock); ++ return -EEXIST; ++ } ++ pr_debug("registering device %s\n", sst_dev->name); ++ ++ sst_dsp = sst_dev; ++ mutex_unlock(&sst_dsp_lock); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(sst_register_dsp); ++ ++int sst_unregister_dsp(struct sst_device *dev) ++{ ++ if (dev != sst_dsp) ++ return -EINVAL; ++ ++ mutex_lock(&sst_dsp_lock); ++ if (sst_dsp) { ++ pr_debug("unregister %s\n", sst_dsp->name); ++ mutex_unlock(&sst_dsp_lock); ++ return -EIO; ++ } ++ ++ sst_dsp = NULL; ++ mutex_unlock(&sst_dsp_lock); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(sst_unregister_dsp); ++ ++static const struct snd_soc_component_driver pcm_component = { ++ .name = "pcm", ++}; ++ ++static int sst_platform_probe(struct platform_device *pdev) ++{ ++ struct sst_data *sst; ++ int ret; ++ struct sst_platform_data *pdata = pdev->dev.platform_data; ++ ++ pr_debug("sst_platform_probe called\n"); ++ sst = devm_kzalloc(&pdev->dev, sizeof(*sst), GFP_KERNEL); ++ if (sst == NULL) { ++ pr_err("kzalloc failed\n"); ++ return -ENOMEM; ++ } ++ ++ sst_pdev = &pdev->dev; ++ sst->pdata = pdata; ++ mutex_init(&sst->lock); ++ dev_set_drvdata(&pdev->dev, sst); ++ ++ ret = snd_soc_register_platform(&pdev->dev, ++ &sst_soc_platform_drv); ++ if (ret) { ++ pr_err("registering soc platform failed\n"); ++ return ret; ++ } ++ ret = snd_soc_register_component(&pdev->dev, &pcm_component, ++ sst_platform_dai, ARRAY_SIZE(sst_platform_dai)); ++ if (ret) { ++ pr_err("registering cpu dais failed\n"); ++ snd_soc_unregister_platform(&pdev->dev); ++ } ++ ++ return ret; ++} ++ ++static int sst_platform_remove(struct platform_device *pdev) ++{ ++ ++ snd_soc_unregister_component(&pdev->dev); ++ snd_soc_unregister_platform(&pdev->dev); ++ pr_debug("sst_platform_remove success\n"); ++ return 0; ++} ++ ++static struct platform_driver sst_platform_driver = { ++ .driver = { ++ .name = "sst-platform", ++ .owner = THIS_MODULE, ++ }, ++ .probe = sst_platform_probe, ++ .remove = sst_platform_remove, ++}; ++ ++module_platform_driver(sst_platform_driver); ++ ++MODULE_DESCRIPTION("ASoC Intel(R) MID Platform driver"); ++MODULE_AUTHOR("Vinod Koul "); ++MODULE_AUTHOR("Harsha Priya "); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:sst-platform"); +diff --git a/sound/soc/intel/platform-libs/controls_v1.c b/sound/soc/intel/platform-libs/controls_v1.c +new file mode 100644 +index 0000000..b6a5fa7 +--- /dev/null ++++ b/sound/soc/intel/platform-libs/controls_v1.c +@@ -0,0 +1,184 @@ ++/* ++ * controls_v1.c - Intel MID Platform driver ALSA controls for CTP ++ * ++ * Copyright (C) 2012 Intel Corp ++ * Author: Jeeja KP ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ */ ++#include ++#include ++#include ++#include "../sst_platform.h" ++#include "../sst_platform_pvt.h" ++ ++ ++static int sst_set_mixer_param(unsigned int device_input_mixer) ++{ ++ if (!sst_dsp) { ++ pr_err("sst: DSP not registered\n"); ++ return -ENODEV; ++ } ++ ++ /*allocate memory for params*/ ++ return sst_dsp->ops->set_generic_params(SST_SET_ALGO_PARAMS, ++ (void *)&device_input_mixer); ++} ++static int lpe_mixer_ihf_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); ++ struct sst_data *sst = snd_soc_platform_get_drvdata(platform); ++ ++ ucontrol->value.integer.value[0] = sst->lpe_mixer_input_ihf; ++ return 0; ++} ++ ++static int lpe_mixer_ihf_set(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ int device_input_mixer; ++ struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); ++ struct sst_data *sst = snd_soc_platform_get_drvdata(platform); ++ ++ switch (ucontrol->value.integer.value[0]) { ++ case 0: ++ pr_debug("input is None\n"); ++ device_input_mixer = SST_STREAM_DEVICE_IHF ++ | SST_INPUT_STREAM_NONE; ++ break; ++ case 1: ++ pr_debug("input is PCM stream\n"); ++ device_input_mixer = SST_STREAM_DEVICE_IHF ++ | SST_INPUT_STREAM_PCM; ++ break; ++ case 2: ++ pr_debug("input is Compress stream\n"); ++ device_input_mixer = SST_STREAM_DEVICE_IHF ++ | SST_INPUT_STREAM_COMPRESS; ++ break; ++ case 3: ++ pr_debug("input is Mixed stream\n"); ++ device_input_mixer = SST_STREAM_DEVICE_IHF ++ | SST_INPUT_STREAM_MIXED; ++ break; ++ default: ++ pr_err("Invalid Input:%ld\n", ucontrol->value.integer.value[0]); ++ return -EINVAL; ++ } ++ sst->lpe_mixer_input_ihf = ucontrol->value.integer.value[0]; ++ return sst_set_mixer_param(device_input_mixer); ++} ++ ++static int lpe_mixer_headset_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); ++ struct sst_data *sst = snd_soc_platform_get_drvdata(platform); ++ ++ ucontrol->value.integer.value[0] = sst->lpe_mixer_input_hs; ++ return 0; ++} ++ ++static int lpe_mixer_headset_set(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ int mixer_input_stream; ++ struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); ++ struct sst_data *sst = snd_soc_platform_get_drvdata(platform); ++ ++ switch (ucontrol->value.integer.value[0]) { ++ case 0: ++ pr_debug("input is None\n"); ++ mixer_input_stream = SST_STREAM_DEVICE_HS ++ | SST_INPUT_STREAM_NONE; ++ break; ++ case 1: ++ pr_debug("input is PCM stream\n"); ++ mixer_input_stream = SST_STREAM_DEVICE_HS ++ | SST_INPUT_STREAM_PCM; ++ break; ++ case 2: ++ pr_debug("input is Compress stream\n"); ++ mixer_input_stream = SST_STREAM_DEVICE_HS ++ | SST_INPUT_STREAM_COMPRESS; ++ break; ++ case 3: ++ pr_debug("input is Mixed stream\n"); ++ mixer_input_stream = SST_STREAM_DEVICE_HS ++ | SST_INPUT_STREAM_MIXED; ++ break; ++ default: ++ pr_err("Invalid Input:%ld\n", ucontrol->value.integer.value[0]); ++ return -EINVAL; ++ } ++ sst->lpe_mixer_input_hs = ucontrol->value.integer.value[0]; ++ return sst_set_mixer_param(mixer_input_stream); ++} ++ ++static int sst_probe_byte_control_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ ++ if (!sst_dsp) { ++ pr_err("sst: DSP not registered\n"); ++ return -ENODEV; ++ } ++ ++ return sst_dsp->ops->set_generic_params(SST_GET_PROBE_BYTE_STREAM, ++ ucontrol->value.bytes.data); ++} ++ ++static int sst_probe_byte_control_set(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ ++ if (!sst_dsp) { ++ pr_err("sst: DSP not registered\n"); ++ return -ENODEV; ++ } ++ ++ return sst_dsp->ops->set_generic_params(SST_SET_PROBE_BYTE_STREAM, ++ ucontrol->value.bytes.data); ++} ++ ++static const char *lpe_mixer_text[] = { ++ "None", "PCM", "Compressed", "Mixed", ++}; ++ ++static const struct soc_enum lpe_mixer_enum = ++ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lpe_mixer_text), lpe_mixer_text); ++ ++static const struct snd_kcontrol_new sst_controls_clv[] = { ++ SOC_ENUM_EXT("LPE IHF mixer", lpe_mixer_enum, ++ lpe_mixer_ihf_get, lpe_mixer_ihf_set), ++ SOC_ENUM_EXT("LPE headset mixer", lpe_mixer_enum, ++ lpe_mixer_headset_get, lpe_mixer_headset_set), ++ SND_SOC_BYTES_EXT("SST Probe Byte Control", SST_MAX_BIN_BYTES, ++ sst_probe_byte_control_get, ++ sst_probe_byte_control_set), ++}; ++ ++int sst_platform_clv_init(struct snd_soc_platform *platform) ++{ ++ struct sst_data *ctx = snd_soc_platform_get_drvdata(platform); ++ ctx->lpe_mixer_input_hs = 0; ++ ctx->lpe_mixer_input_ihf = 0; ++ snd_soc_add_platform_controls(platform, sst_controls_clv, ++ ARRAY_SIZE(sst_controls_clv)); ++ return 0; ++} +diff --git a/sound/soc/intel/platform-libs/controls_v2.c b/sound/soc/intel/platform-libs/controls_v2.c +new file mode 100644 +index 0000000..3e80276 +--- /dev/null ++++ b/sound/soc/intel/platform-libs/controls_v2.c +@@ -0,0 +1,1770 @@ ++/* ++ * controls_v2.c - Intel MID Platform driver ALSA controls for Mrfld ++ * ++ * Copyright (C) 2012 Intel Corp ++ * Author: Vinod Koul ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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 ++ ++#include ++#include ++#include ++#include "../platform_ipc_v2.h" ++#include "../sst_platform.h" ++#include "../sst_platform_pvt.h" ++#include "ipc_lib.h" ++#include "controls_v2.h" ++ ++ ++#define SST_ALGO_KCONTROL_INT(xname, xreg, xshift, xmax, xinvert,\ ++ xhandler_get, xhandler_put, xmod, xpipe, xinstance, default_val) \ ++{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ ++ .info = sst_algo_int_ctl_info, \ ++ .get = xhandler_get, .put = xhandler_put, \ ++ .private_value = (unsigned long)&(struct sst_algo_int_control_v2) \ ++ {.mc.reg = xreg, .mc.rreg = xreg, .mc.shift = xshift, \ ++ .mc.rshift = xshift, .mc.max = xmax, .mc.platform_max = xmax, \ ++ .mc.invert = xinvert, .module_id = xmod, .pipe_id = xpipe, \ ++ .instance_id = xinstance, .value = default_val } } ++/* Thresholds for Low Latency & Deep Buffer*/ ++#define DEFAULT_LOW_LATENCY 10 /* In Ms */ ++#define DEFAULT_DEEP_BUFFER 96 ++ ++unsigned long ll_threshold = DEFAULT_LOW_LATENCY; ++unsigned long db_threshold = DEFAULT_DEEP_BUFFER; ++ ++int sst_algo_int_ctl_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ struct sst_algo_int_control_v2 *amc = (void *)kcontrol->private_value; ++ struct soc_mixer_control *mc = &amc->mc; ++ int platform_max; ++ ++ if (!mc->platform_max) ++ mc->platform_max = mc->max; ++ platform_max = mc->platform_max; ++ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = 1; ++ uinfo->value.integer.min = 0; ++ uinfo->value.integer.max = platform_max; ++ return 0; ++} ++ ++unsigned int sst_soc_read(struct snd_soc_platform *platform, ++ unsigned int reg) ++{ ++ struct sst_data *sst = snd_soc_platform_get_drvdata(platform); ++ ++ pr_debug("%s for reg %d val=%d\n", __func__, reg, sst->widget[reg]); ++ BUG_ON(reg > (SST_NUM_WIDGETS - 1)); ++ return sst->widget[reg]; ++} ++ ++int sst_soc_write(struct snd_soc_platform *platform, ++ unsigned int reg, unsigned int val) ++{ ++ struct sst_data *sst = snd_soc_platform_get_drvdata(platform); ++ ++ pr_debug("%s for reg %d val %d\n", __func__, reg, val); ++ BUG_ON(reg > (SST_NUM_WIDGETS - 1)); ++ sst->widget[reg] = val; ++ return 0; ++} ++ ++unsigned int sst_reg_read(struct sst_data *sst, unsigned int reg, ++ unsigned int shift, unsigned int max) ++{ ++ unsigned int mask = (1 << fls(max)) - 1; ++ ++ return (sst->widget[reg] >> shift) & mask; ++} ++ ++unsigned int sst_reg_write(struct sst_data *sst, unsigned int reg, ++ unsigned int shift, unsigned int max, unsigned int val) ++{ ++ unsigned int mask = (1 << fls(max)) - 1; ++ ++ val &= mask; ++ val <<= shift; ++ sst->widget[reg] &= ~(mask << shift); ++ sst->widget[reg] |= val; ++ return val; ++} ++ ++int sst_mix_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); ++ struct snd_soc_dapm_widget *widget = wlist->widgets[0]; ++ struct soc_mixer_control *mc = ++ (struct soc_mixer_control *)kcontrol->private_value; ++ struct sst_data *sst = snd_soc_platform_get_drvdata(widget->platform); ++ unsigned int mask = (1 << fls(mc->max)) - 1; ++ unsigned int val; ++ int connect; ++ struct snd_soc_dapm_update update; ++ ++ pr_debug("%s called set %ld for %s\n", __func__, ++ ucontrol->value.integer.value[0], widget->name); ++ val = sst_reg_write(sst, mc->reg, mc->shift, mc->max, ucontrol->value.integer.value[0]); ++ connect = !!val; ++ ++ widget->value = val; ++ update.kcontrol = kcontrol; ++ update.widget = widget; ++ update.reg = mc->reg; ++ update.mask = mask; ++ update.val = val; ++ ++ widget->dapm->update = &update; ++ snd_soc_dapm_mixer_update_power(widget, kcontrol, connect); ++ widget->dapm->update = NULL; ++ return 0; ++} ++ ++int sst_mix_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); ++ struct snd_soc_dapm_widget *w = wlist->widgets[0]; ++ struct soc_mixer_control *mc = ++ (struct soc_mixer_control *)kcontrol->private_value; ++ struct sst_data *sst = snd_soc_platform_get_drvdata(w->platform); ++ ++ pr_debug("%s called for %s\n", __func__, w->name); ++ ucontrol->value.integer.value[0] = !!sst_reg_read(sst, mc->reg, mc->shift, mc->max); ++ return 0; ++} ++ ++static const struct snd_kcontrol_new sst_mix_modem_controls[] = { ++ SOC_SINGLE_EXT("Modem", SST_MIX_MODEM, 0, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("BT", SST_MIX_MODEM, 1, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec0", SST_MIX_MODEM, 2, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec1", SST_MIX_MODEM, 3, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sprot_L0", SST_MIX_MODEM, 4, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L1", SST_MIX_MODEM, 5, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L2", SST_MIX_MODEM, 6, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Probe", SST_MIX_MODEM, 7, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sidetone", SST_MIX_MODEM, 8, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Tx", SST_MIX_MODEM, 9, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Rx", SST_MIX_MODEM, 10, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Tone", SST_MIX_MODEM, 11, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Voip", SST_MIX_MODEM, 12, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM0", SST_MIX_MODEM, 13, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM1", SST_MIX_MODEM, 14, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media0", SST_MIX_MODEM, 15, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media1", SST_MIX_MODEM, 16, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media2", SST_MIX_MODEM, 17, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("FM", SST_MIX_MODEM, 18, 1, 0, ++ sst_mix_get, sst_mix_put), ++}; ++ ++static const struct snd_kcontrol_new sst_mix_codec0_controls[] = { ++ SOC_SINGLE_EXT("Modem", SST_MIX_CODEC0, 0, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("BT", SST_MIX_CODEC0, 1, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec0", SST_MIX_CODEC0, 2, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec1", SST_MIX_CODEC0, 3, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sprot_L0", SST_MIX_CODEC0, 4, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L1", SST_MIX_CODEC0, 5, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L2", SST_MIX_CODEC0, 6, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Probe", SST_MIX_CODEC0, 7, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sidetone", SST_MIX_CODEC0, 8, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Tx", SST_MIX_CODEC0, 9, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Rx", SST_MIX_CODEC0, 10, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Tone", SST_MIX_CODEC0, 11, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Voip", SST_MIX_CODEC0, 12, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM0", SST_MIX_CODEC0, 13, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM1", SST_MIX_CODEC0, 14, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media0", SST_MIX_CODEC0, 15, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media1", SST_MIX_CODEC0, 16, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media2", SST_MIX_CODEC0, 17, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("FM", SST_MIX_CODEC0, 18, 1, 0, ++ sst_mix_get, sst_mix_put), ++}; ++ ++static const struct snd_kcontrol_new sst_mix_codec1_controls[] = { ++ SOC_SINGLE_EXT("Modem", SST_MIX_CODEC1, 0, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("BT", SST_MIX_CODEC1, 1, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec0", SST_MIX_CODEC1, 2, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec1", SST_MIX_CODEC1, 3, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sprot_L0", SST_MIX_CODEC1, 4, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L1", SST_MIX_CODEC1, 5, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L2", SST_MIX_CODEC1, 6, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Probe", SST_MIX_CODEC1, 7, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sidetone", SST_MIX_CODEC1, 8, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Tx", SST_MIX_CODEC1, 9, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Rx", SST_MIX_CODEC1, 10, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Tone", SST_MIX_CODEC1, 11, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Voip", SST_MIX_CODEC1, 12, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM0", SST_MIX_CODEC1, 13, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM1", SST_MIX_CODEC1, 14, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media0", SST_MIX_CODEC1, 15, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media1", SST_MIX_CODEC1, 16, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media2", SST_MIX_CODEC1, 17, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("FM", SST_MIX_CODEC1, 18, 1, 0, ++ sst_mix_get, sst_mix_put), ++}; ++ ++static const struct snd_kcontrol_new sst_mix_sprot_l0_controls[] = { ++ SOC_SINGLE_EXT("Modem", SST_MIX_LOOP0, 0, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("BT", SST_MIX_LOOP0, 1, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec0", SST_MIX_LOOP0, 2, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec1", SST_MIX_LOOP0, 3, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sprot_L0", SST_MIX_LOOP0, 4, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L1", SST_MIX_LOOP0, 5, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L2", SST_MIX_LOOP0, 6, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Probe", SST_MIX_LOOP0, 7, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sidetone", SST_MIX_LOOP0, 8, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Tx", SST_MIX_LOOP0, 9, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Rx", SST_MIX_LOOP0, 10, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Tone", SST_MIX_LOOP0, 11, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Voip", SST_MIX_LOOP0, 12, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM0", SST_MIX_LOOP0, 13, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM1", SST_MIX_LOOP0, 14, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media0", SST_MIX_LOOP0, 15, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media1", SST_MIX_LOOP0, 16, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media2", SST_MIX_LOOP0, 17, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("FM", SST_MIX_LOOP0, 18, 1, 0, ++ sst_mix_get, sst_mix_put), ++}; ++ ++static const struct snd_kcontrol_new sst_mix_media_l1_controls[] = { ++ SOC_SINGLE_EXT("Modem", SST_MIX_LOOP1, 0, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("BT", SST_MIX_LOOP1, 1, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec0", SST_MIX_LOOP1, 2, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec1", SST_MIX_LOOP1, 3, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sprot_L0", SST_MIX_LOOP1, 4, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L1", SST_MIX_LOOP1, 5, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L2", SST_MIX_LOOP1, 6, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Probe", SST_MIX_LOOP1, 7, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sidetone", SST_MIX_LOOP1, 8, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Tx", SST_MIX_LOOP1, 9, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Rx", SST_MIX_LOOP1, 10, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Tone", SST_MIX_LOOP1, 11, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Voip", SST_MIX_LOOP1, 12, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM0", SST_MIX_LOOP1, 13, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM1", SST_MIX_LOOP1, 14, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media0", SST_MIX_LOOP1, 15, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media1", SST_MIX_LOOP1, 16, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media2", SST_MIX_LOOP1, 17, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("FM", SST_MIX_LOOP1, 18, 1, 0, ++ sst_mix_get, sst_mix_put), ++}; ++ ++static const struct snd_kcontrol_new sst_mix_media_l2_controls[] = { ++ SOC_SINGLE_EXT("Modem", SST_MIX_LOOP2, 0, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("BT", SST_MIX_LOOP2, 1, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec0", SST_MIX_LOOP2, 2, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec1", SST_MIX_LOOP2, 3, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sprot_L0", SST_MIX_LOOP2, 4, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L1", SST_MIX_LOOP2, 5, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L2", SST_MIX_LOOP2, 6, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Probe", SST_MIX_LOOP2, 7, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sidetone", SST_MIX_LOOP2, 8, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Tx", SST_MIX_LOOP2, 9, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Rx", SST_MIX_LOOP2, 10, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Tone", SST_MIX_LOOP2, 11, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Voip", SST_MIX_LOOP2, 12, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM0", SST_MIX_LOOP2, 13, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM1", SST_MIX_LOOP2, 14, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media0", SST_MIX_LOOP2, 15, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media1", SST_MIX_LOOP2, 16, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media2", SST_MIX_LOOP2, 17, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("FM", SST_MIX_LOOP2, 18, 1, 0, ++ sst_mix_get, sst_mix_put), ++}; ++ ++static const struct snd_kcontrol_new sst_mix_speech_tx_controls[] = { ++ SOC_SINGLE_EXT("Modem", SST_MIX_SPEECH, 0, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("BT", SST_MIX_SPEECH, 1, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec0", SST_MIX_SPEECH, 2, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec1", SST_MIX_SPEECH, 3, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sprot_L0", SST_MIX_SPEECH, 4, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L1", SST_MIX_SPEECH, 5, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L2", SST_MIX_SPEECH, 6, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Probe", SST_MIX_SPEECH, 7, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sidetone", SST_MIX_SPEECH, 8, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Tx", SST_MIX_SPEECH, 9, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Rx", SST_MIX_SPEECH, 10, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Tone", SST_MIX_SPEECH, 11, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Voip", SST_MIX_SPEECH, 12, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM0", SST_MIX_SPEECH, 13, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM1", SST_MIX_SPEECH, 14, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media0", SST_MIX_SPEECH, 15, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media1", SST_MIX_SPEECH, 16, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media2", SST_MIX_SPEECH, 17, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("FM", SST_MIX_SPEECH, 18, 1, 0, ++ sst_mix_get, sst_mix_put), ++}; ++ ++static const struct snd_kcontrol_new sst_mix_speech_rx_controls[] = { ++ SOC_SINGLE_EXT("Modem", SST_MIX_RXSPEECH, 0, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("BT", SST_MIX_RXSPEECH, 1, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec0", SST_MIX_RXSPEECH, 2, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec1", SST_MIX_RXSPEECH, 3, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sprot_L0", SST_MIX_RXSPEECH, 4, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L1", SST_MIX_RXSPEECH, 5, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L2", SST_MIX_RXSPEECH, 6, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Probe", SST_MIX_RXSPEECH, 7, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sidetone", SST_MIX_RXSPEECH, 8, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Tx", SST_MIX_RXSPEECH, 9, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Rx", SST_MIX_RXSPEECH, 10, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Tone", SST_MIX_RXSPEECH, 11, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Voip", SST_MIX_RXSPEECH, 12, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM0", SST_MIX_RXSPEECH, 13, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM1", SST_MIX_RXSPEECH, 14, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media0", SST_MIX_RXSPEECH, 15, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media1", SST_MIX_RXSPEECH, 16, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media2", SST_MIX_RXSPEECH, 17, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("FM", SST_MIX_RXSPEECH, 18, 1, 0, ++ sst_mix_get, sst_mix_put), ++}; ++ ++static const struct snd_kcontrol_new sst_mix_voip_controls[] = { ++ SOC_SINGLE_EXT("Modem", SST_MIX_VOIP, 0, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("BT", SST_MIX_VOIP, 1, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec0", SST_MIX_VOIP, 2, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec1", SST_MIX_VOIP, 3, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sprot_L0", SST_MIX_VOIP, 4, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L1", SST_MIX_VOIP, 5, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L2", SST_MIX_VOIP, 6, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Probe", SST_MIX_VOIP, 7, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sidetone", SST_MIX_VOIP, 8, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Tx", SST_MIX_VOIP, 9, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Rx", SST_MIX_VOIP, 10, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Tone", SST_MIX_VOIP, 11, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Voip", SST_MIX_VOIP, 12, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM0", SST_MIX_VOIP, 13, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM1", SST_MIX_VOIP, 14, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media0", SST_MIX_VOIP, 15, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media1", SST_MIX_VOIP, 16, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media2", SST_MIX_VOIP, 17, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("FM", SST_MIX_VOIP, 18, 1, 0, ++ sst_mix_get, sst_mix_put), ++}; ++ ++static const struct snd_kcontrol_new sst_mix_pcm0_controls[] = { ++ SOC_SINGLE_EXT("Modem", SST_MIX_PCM0, 0, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("BT", SST_MIX_PCM0, 1, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec0", SST_MIX_PCM0, 2, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec1", SST_MIX_PCM0, 3, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sprot_L0", SST_MIX_PCM0, 4, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L1", SST_MIX_PCM0, 5, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L2", SST_MIX_PCM0, 6, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Probe", SST_MIX_PCM0, 7, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sidetone", SST_MIX_PCM0, 8, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Tx", SST_MIX_PCM0, 9, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Rx", SST_MIX_PCM0, 10, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Tone", SST_MIX_PCM0, 11, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Voip", SST_MIX_PCM0, 12, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM0", SST_MIX_PCM0, 13, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM1", SST_MIX_PCM0, 14, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media0", SST_MIX_PCM0, 15, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media1", SST_MIX_PCM0, 16, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media2", SST_MIX_PCM0, 17, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("FM", SST_MIX_PCM0, 18, 1, 0, ++ sst_mix_get, sst_mix_put), ++}; ++ ++static const struct snd_kcontrol_new sst_mix_pcm1_controls[] = { ++ SOC_SINGLE_EXT("Modem", SST_MIX_PCM1, 0, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("BT", SST_MIX_PCM1, 1, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec0", SST_MIX_PCM1, 2, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec1", SST_MIX_PCM1, 3, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sprot_L0", SST_MIX_PCM1, 4, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L1", SST_MIX_PCM1, 5, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L2", SST_MIX_PCM1, 6, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Probe", SST_MIX_PCM1, 7, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sidetone", SST_MIX_PCM1, 8, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Tx", SST_MIX_PCM1, 9, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Rx", SST_MIX_PCM1, 10, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Tone", SST_MIX_PCM1, 11, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Voip", SST_MIX_PCM1, 12, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM0", SST_MIX_PCM1, 13, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM1", SST_MIX_PCM1, 14, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media0", SST_MIX_PCM1, 15, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media1", SST_MIX_PCM1, 16, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media2", SST_MIX_PCM1, 17, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("FM", SST_MIX_PCM1, 18, 1, 0, ++ sst_mix_get, sst_mix_put), ++}; ++ ++static const struct snd_kcontrol_new sst_mix_pcm2_controls[] = { ++ SOC_SINGLE_EXT("Modem", SST_MIX_PCM2, 0, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("BT", SST_MIX_PCM2, 1, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec0", SST_MIX_PCM2, 2, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec1", SST_MIX_PCM2, 3, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sprot_L0", SST_MIX_PCM2, 4, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L1", SST_MIX_PCM2, 5, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L2", SST_MIX_PCM2, 6, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Probe", SST_MIX_PCM2, 7, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sidetone", SST_MIX_PCM2, 8, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Tx", SST_MIX_PCM2, 9, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Rx", SST_MIX_PCM2, 10, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Tone", SST_MIX_PCM2, 11, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Voip", SST_MIX_PCM2, 12, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM0", SST_MIX_PCM2, 13, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM1", SST_MIX_PCM2, 14, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media0", SST_MIX_PCM2, 15, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media1", SST_MIX_PCM2, 16, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media2", SST_MIX_PCM2, 17, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("FM", SST_MIX_PCM2, 18, 1, 0, ++ sst_mix_get, sst_mix_put), ++}; ++ ++static const struct snd_kcontrol_new sst_mix_aware_controls[] = { ++ SOC_SINGLE_EXT("Modem", SST_MIX_AWARE, 0, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("BT", SST_MIX_AWARE, 1, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec0", SST_MIX_AWARE, 2, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec1", SST_MIX_AWARE, 3, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sprot_L0", SST_MIX_AWARE, 4, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L1", SST_MIX_AWARE, 5, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L2", SST_MIX_AWARE, 6, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Probe", SST_MIX_AWARE, 7, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sidetone", SST_MIX_AWARE, 8, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Tx", SST_MIX_AWARE, 9, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Rx", SST_MIX_AWARE, 10, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Tone", SST_MIX_AWARE, 11, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Voip", SST_MIX_AWARE, 12, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM0", SST_MIX_AWARE, 13, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM1", SST_MIX_AWARE, 14, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media0", SST_MIX_AWARE, 15, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media1", SST_MIX_AWARE, 16, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media2", SST_MIX_AWARE, 17, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("FM", SST_MIX_AWARE, 18, 1, 0, ++ sst_mix_get, sst_mix_put), ++}; ++ ++static const struct snd_kcontrol_new sst_mix_vad_controls[] = { ++ SOC_SINGLE_EXT("Modem", SST_MIX_VAD, 0, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("BT", SST_MIX_VAD, 1, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec0", SST_MIX_VAD, 2, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec1", SST_MIX_VAD, 3, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sprot_L0", SST_MIX_VAD, 4, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L1", SST_MIX_VAD, 5, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L2", SST_MIX_VAD, 6, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Probe", SST_MIX_VAD, 7, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sidetone", SST_MIX_VAD, 8, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Tx", SST_MIX_VAD, 9, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Rx", SST_MIX_VAD, 10, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Tone", SST_MIX_VAD, 11, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Voip", SST_MIX_VAD, 12, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM0", SST_MIX_VAD, 13, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM1", SST_MIX_VAD, 14, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media0", SST_MIX_VAD, 15, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media1", SST_MIX_VAD, 16, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media2", SST_MIX_VAD, 17, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("FM", SST_MIX_VAD, 18, 1, 0, ++ sst_mix_get, sst_mix_put), ++}; ++ ++static const struct snd_kcontrol_new sst_mix_media0_controls[] = { ++ SOC_SINGLE_EXT("Modem", SST_MIX_MEDIA0, 0, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("BT", SST_MIX_MEDIA0, 1, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec0", SST_MIX_MEDIA0, 2, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec1", SST_MIX_MEDIA0, 3, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sprot_L0", SST_MIX_MEDIA0, 4, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L1", SST_MIX_MEDIA0, 5, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L2", SST_MIX_MEDIA0, 6, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Probe", SST_MIX_MEDIA0, 7, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sidetone", SST_MIX_MEDIA0, 8, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Tx", SST_MIX_MEDIA0, 9, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Rx", SST_MIX_MEDIA0, 10, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Tone", SST_MIX_MEDIA0, 11, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Voip", SST_MIX_MEDIA0, 12, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM0", SST_MIX_MEDIA0, 13, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM1", SST_MIX_MEDIA0, 14, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media0", SST_MIX_MEDIA0, 15, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media1", SST_MIX_MEDIA0, 16, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media2", SST_MIX_MEDIA0, 17, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("FM", SST_MIX_MEDIA0, 18, 1, 0, ++ sst_mix_get, sst_mix_put), ++}; ++ ++static const struct snd_kcontrol_new sst_mix_media1_controls[] = { ++ SOC_SINGLE_EXT("Modem", SST_MIX_MEDIA1, 0, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("BT", SST_MIX_MEDIA1, 1, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec0", SST_MIX_MEDIA1, 2, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec1", SST_MIX_MEDIA1, 3, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sprot_L0", SST_MIX_MEDIA1, 4, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L1", SST_MIX_MEDIA1, 5, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L2", SST_MIX_MEDIA1, 6, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Probe", SST_MIX_MEDIA1, 7, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sidetone", SST_MIX_MEDIA1, 8, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Tx", SST_MIX_MEDIA1, 9, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Rx", SST_MIX_MEDIA1, 10, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Tone", SST_MIX_MEDIA1, 11, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Voip", SST_MIX_MEDIA1, 12, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM0", SST_MIX_MEDIA1, 13, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM1", SST_MIX_MEDIA1, 14, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media0", SST_MIX_MEDIA1, 15, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media1", SST_MIX_MEDIA1, 16, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media2", SST_MIX_MEDIA1, 17, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("FM", SST_MIX_MEDIA1, 18, 1, 0, ++ sst_mix_get, sst_mix_put), ++}; ++ ++static const struct snd_kcontrol_new sst_mix_fm_controls[] = { ++ SOC_SINGLE_EXT("Modem", SST_MIX_FM, 0, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("BT", SST_MIX_FM, 1, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec0", SST_MIX_FM, 2, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Codec1", SST_MIX_FM, 3, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sprot_L0", SST_MIX_FM, 4, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L1", SST_MIX_FM, 5, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media_L2", SST_MIX_FM, 6, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Probe", SST_MIX_FM, 7, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Sidetone", SST_MIX_FM, 8, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Tx", SST_MIX_FM, 9, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Speech_Rx", SST_MIX_FM, 10, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Tone", SST_MIX_FM, 11, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Voip", SST_MIX_FM, 12, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM0", SST_MIX_FM, 13, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("PCM1", SST_MIX_FM, 14, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media0", SST_MIX_FM, 15, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media1", SST_MIX_FM, 16, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("Media2", SST_MIX_FM, 17, 1, 0, ++ sst_mix_get, sst_mix_put), ++ SOC_SINGLE_EXT("FM", SST_MIX_FM, 18, 1, 0, ++ sst_mix_get, sst_mix_put), ++}; ++ ++static const struct snd_kcontrol_new sst_mix_sw_modem = ++ SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 0, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_mix_sw_codec0 = ++ SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 1, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_mix_sw_codec1 = ++ SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 2, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_mix_sw_sprot_l0 = ++ SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 3, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_mix_sw_media_l1 = ++ SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 4, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_mix_sw_media_l2 = ++ SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 5, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_mix_sw_speech_tx = ++ SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 6, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_mix_sw_speech_rx = ++ SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 7, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_mix_sw_voip = ++ SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 8, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_mix_sw_pcm0 = ++ SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 9, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_mix_sw_pcm1 = ++ SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 10, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_mix_sw_pcm2 = ++ SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 11, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_mix_sw_aware = ++ SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 12, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_mix_sw_vad = ++ SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 13, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_mix_sw_media0 = ++ SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 14, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_mix_sw_media1 = ++ SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 15, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_mix_sw_fm = ++ SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 16, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_out_sw_modem = ++ SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 0, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_out_sw_codec0 = ++ SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 1, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_out_sw_codec1 = ++ SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 2, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_out_sw_speech_tx = ++ SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 6, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_out_sw_speech_rx = ++ SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 7, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_out_sw_voip = ++ SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 8, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_out_sw_pcm0 = ++ SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 9, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_out_sw_pcm1 = ++ SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 10, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_out_sw_pcm2 = ++ SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 11, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_out_sw_aware = ++ SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 12, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_out_sw_vad = ++ SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 13, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_out_sw_media0 = ++ SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 14, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_out_sw_media1 = ++ SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 15, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_out_sw_fm = ++ SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 16, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_in_sw_modem = ++ SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 0, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_in_sw_codec0 = ++ SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 1, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_in_sw_codec1 = ++ SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 2, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_in_sw_sidetone = ++ SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 3, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_in_sw_speech_tx = ++ SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 4, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_in_sw_speech_rx = ++ SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 5, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_in_sw_tone = ++ SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 6, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_in_sw_voip = ++ SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 7, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_in_sw_pcm0 = ++ SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 8, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_in_sw_pcm1 = ++ SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 9, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_in_sw_media0 = ++ SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 10, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_in_sw_media1 = ++ SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 11, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_in_sw_media2 = ++ SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 12, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_kcontrol_new sst_in_sw_fm = ++ SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 13, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const struct snd_soc_dapm_widget sst_dapm_widgets[] = { ++ SND_SOC_DAPM_INPUT("Modem IN"), ++ SND_SOC_DAPM_INPUT("Codec IN0"), ++ SND_SOC_DAPM_INPUT("Codec IN1"), ++ SND_SOC_DAPM_INPUT("Tone IN"), ++ SND_SOC_DAPM_INPUT("FM IN"), ++ SND_SOC_DAPM_OUTPUT("Modem OUT"), ++ SND_SOC_DAPM_OUTPUT("Codec OUT0"), ++ SND_SOC_DAPM_OUTPUT("Codec OUT1"), ++ SND_SOC_DAPM_OUTPUT("FM OUT"), ++ SND_SOC_DAPM_AIF_IN("Voip IN", "VoIP", 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_IN("Media IN0", "Compress", 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_IN("Media IN1", "PCM", 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_OUT("Voip OUT", "VoIP", 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_OUT("PCM1 OUT", "Capture", 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_OUT("Aware OUT", "Aware", 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_OUT("VAD OUT", "VAD", 0, SND_SOC_NOPM, 0, 0), ++ ++ /* output mixers */ ++ SND_SOC_DAPM_MIXER("MIX Modem", SND_SOC_NOPM, 0, 0, ++ sst_mix_modem_controls, ARRAY_SIZE(sst_mix_modem_controls)), ++ SND_SOC_DAPM_MIXER("MIX Codec0", SND_SOC_NOPM, 0, 0, ++ sst_mix_codec0_controls , ARRAY_SIZE(sst_mix_codec0_controls)), ++ SND_SOC_DAPM_MIXER("MIX Codec1", SND_SOC_NOPM, 0, 0, ++ sst_mix_codec1_controls, ARRAY_SIZE(sst_mix_codec1_controls)), ++ SND_SOC_DAPM_MIXER("MIX Sprot L0", SND_SOC_NOPM, 0, 0, ++ sst_mix_sprot_l0_controls, ARRAY_SIZE(sst_mix_sprot_l0_controls)), ++ SND_SOC_DAPM_MIXER("MIX Media L1", SND_SOC_NOPM, 0, 0, ++ sst_mix_media_l1_controls, ARRAY_SIZE(sst_mix_media_l1_controls)), ++ SND_SOC_DAPM_MIXER("MIX Media L2", SND_SOC_NOPM, 0, 0, ++ sst_mix_media_l2_controls, ARRAY_SIZE(sst_mix_media_l2_controls)), ++ SND_SOC_DAPM_MIXER("MIX Speech Tx", SND_SOC_NOPM, 0, 0, ++ sst_mix_speech_tx_controls, ARRAY_SIZE(sst_mix_speech_tx_controls)), ++ SND_SOC_DAPM_MIXER("MIX Speech Rx", SND_SOC_NOPM, 0, 0, ++ sst_mix_speech_rx_controls, ARRAY_SIZE(sst_mix_speech_rx_controls)), ++ SND_SOC_DAPM_MIXER("MIX Voip", SND_SOC_NOPM, 0, 0, ++ sst_mix_voip_controls, ARRAY_SIZE(sst_mix_voip_controls)), ++ SND_SOC_DAPM_MIXER("MIX PCM0", SND_SOC_NOPM, 0, 0, ++ sst_mix_pcm0_controls, ARRAY_SIZE(sst_mix_pcm0_controls)), ++ SND_SOC_DAPM_MIXER("MIX PCM1", SND_SOC_NOPM, 0, 0, ++ sst_mix_pcm1_controls, ARRAY_SIZE(sst_mix_pcm1_controls)), ++ SND_SOC_DAPM_MIXER("MIX PCM2", SND_SOC_NOPM, 0, 0, ++ sst_mix_pcm2_controls, ARRAY_SIZE(sst_mix_pcm2_controls)), ++ SND_SOC_DAPM_MIXER("MIX Aware", SND_SOC_NOPM, 0, 0, ++ sst_mix_aware_controls, ARRAY_SIZE(sst_mix_aware_controls)), ++ SND_SOC_DAPM_MIXER("MIX VAD", SND_SOC_NOPM, 0, 0, ++ sst_mix_vad_controls, ARRAY_SIZE(sst_mix_vad_controls)), ++ SND_SOC_DAPM_MIXER("MIX Media0", SND_SOC_NOPM, 0, 0, ++ sst_mix_media0_controls, ARRAY_SIZE(sst_mix_media0_controls)), ++ SND_SOC_DAPM_MIXER("MIX Media1", SND_SOC_NOPM, 0, 0, ++ sst_mix_media1_controls, ARRAY_SIZE(sst_mix_media1_controls)), ++ SND_SOC_DAPM_MIXER("MIX FM", SND_SOC_NOPM, 0, 0, ++ sst_mix_fm_controls, ARRAY_SIZE(sst_mix_fm_controls)), ++ ++ /* switches for mixer outputs */ ++ SND_SOC_DAPM_SWITCH("Mix Modem Switch", SND_SOC_NOPM, 0, 0, ++ &sst_mix_sw_modem), ++ SND_SOC_DAPM_SWITCH("Mix Codec0 Switch", SND_SOC_NOPM, 0, 0, ++ &sst_mix_sw_codec0), ++ SND_SOC_DAPM_SWITCH("Mix Codec1 Switch", SND_SOC_NOPM, 0, 0, ++ &sst_mix_sw_codec1), ++ SND_SOC_DAPM_SWITCH("Mix Sprot L0 Switch", SND_SOC_NOPM, 0, 0, ++ &sst_mix_sw_sprot_l0), ++ SND_SOC_DAPM_SWITCH("Mix Media L1 Switch", SND_SOC_NOPM, 0, 0, ++ &sst_mix_sw_media_l1), ++ SND_SOC_DAPM_SWITCH("Mix Media L2 Switch", SND_SOC_NOPM, 0, 0, ++ &sst_mix_sw_media_l2), ++ SND_SOC_DAPM_SWITCH("Mix Speech Tx Switch", SND_SOC_NOPM, 0, 0, ++ &sst_mix_sw_speech_tx), ++ SND_SOC_DAPM_SWITCH("Mix Speech Rx Switch", SND_SOC_NOPM, 0, 0, ++ &sst_mix_sw_speech_rx), ++ SND_SOC_DAPM_SWITCH("Mix Voip Switch", SND_SOC_NOPM, 0, 0, ++ &sst_mix_sw_voip), ++ SND_SOC_DAPM_SWITCH("Mix PCM0 Switch", SND_SOC_NOPM, 0, 0, ++ &sst_mix_sw_pcm0), ++ SND_SOC_DAPM_SWITCH("Mix PCM1 Switch", SND_SOC_NOPM, 0, 0, ++ &sst_mix_sw_pcm1), ++ SND_SOC_DAPM_SWITCH("Mix PCM2 Switch", SND_SOC_NOPM, 0, 0, ++ &sst_mix_sw_pcm2), ++ SND_SOC_DAPM_SWITCH("Mix Aware Switch", SND_SOC_NOPM, 0, 0, ++ &sst_mix_sw_aware), ++ SND_SOC_DAPM_SWITCH("Mix VAD Switch", SND_SOC_NOPM, 0, 0, ++ &sst_mix_sw_vad), ++ SND_SOC_DAPM_SWITCH("Mix Media0 Switch", SND_SOC_NOPM, 0, 0, ++ &sst_mix_sw_media0), ++ SND_SOC_DAPM_SWITCH("Mix Media1 Switch", SND_SOC_NOPM, 0, 0, ++ &sst_mix_sw_media1), ++ SND_SOC_DAPM_SWITCH("Mix FM Switch", SND_SOC_NOPM, 0, 0, ++ &sst_mix_sw_fm), ++ ++ /* output pipeline switches */ ++ SND_SOC_DAPM_SWITCH("Out Modem Switch", SND_SOC_NOPM, 0, 0, ++ &sst_out_sw_modem), ++ SND_SOC_DAPM_SWITCH("Out Codec0 Switch", SND_SOC_NOPM, 0, 0, ++ &sst_out_sw_codec0), ++ SND_SOC_DAPM_SWITCH("Out Codec1 Switch", SND_SOC_NOPM, 0, 0, ++ &sst_out_sw_codec1), ++ SND_SOC_DAPM_SWITCH("Out Speech Tx Switch", SND_SOC_NOPM, 0, 0, ++ &sst_out_sw_speech_tx), ++ SND_SOC_DAPM_SWITCH("Out Speech Rx Switch", SND_SOC_NOPM, 0, 0, ++ &sst_out_sw_speech_rx), ++ SND_SOC_DAPM_SWITCH("Out Voip Switch", SND_SOC_NOPM, 0, 0, ++ &sst_out_sw_voip), ++ SND_SOC_DAPM_SWITCH("Out PCM0 Switch", SND_SOC_NOPM, 0, 0, ++ &sst_out_sw_pcm0), ++ SND_SOC_DAPM_SWITCH("Out PCM1 Switch", SND_SOC_NOPM, 0, 0, ++ &sst_out_sw_pcm1), ++ SND_SOC_DAPM_SWITCH("Out PCM2 Switch", SND_SOC_NOPM, 0, 0, ++ &sst_out_sw_pcm2), ++ SND_SOC_DAPM_SWITCH("Out Aware Switch", SND_SOC_NOPM, 0, 0, ++ &sst_out_sw_aware), ++ SND_SOC_DAPM_SWITCH("Out VAD Switch", SND_SOC_NOPM, 0, 0, ++ &sst_out_sw_vad), ++ SND_SOC_DAPM_SWITCH("Out Media0 Switch", SND_SOC_NOPM, 0, 0, ++ &sst_out_sw_media0), ++ SND_SOC_DAPM_SWITCH("Out Media1 Switch", SND_SOC_NOPM, 0, 0, ++ &sst_out_sw_media1), ++ SND_SOC_DAPM_SWITCH("Out FM Switch", SND_SOC_NOPM, 0, 0, ++ &sst_out_sw_fm), ++ ++ /* Input pipeline switches */ ++ SND_SOC_DAPM_SWITCH("In Modem Switch", SND_SOC_NOPM, 0, 0, ++ &sst_in_sw_modem), ++ SND_SOC_DAPM_SWITCH("In Codec0 Switch", SND_SOC_NOPM, 0, 0, ++ &sst_in_sw_codec0), ++ SND_SOC_DAPM_SWITCH("In Codec1 Switch", SND_SOC_NOPM, 0, 0, ++ &sst_in_sw_codec1), ++ SND_SOC_DAPM_SWITCH("In Speech Tx Switch", SND_SOC_NOPM, 0, 0, ++ &sst_in_sw_speech_tx), ++ SND_SOC_DAPM_SWITCH("In Speech Rx Switch", SND_SOC_NOPM, 0, 0, ++ &sst_in_sw_speech_rx), ++ SND_SOC_DAPM_SWITCH("In Tone Switch", SND_SOC_NOPM, 0, 0, ++ &sst_in_sw_tone), ++ SND_SOC_DAPM_SWITCH("In Voip Switch", SND_SOC_NOPM, 0, 0, ++ &sst_in_sw_voip), ++ SND_SOC_DAPM_SWITCH("In PCM0 Switch", SND_SOC_NOPM, 0, 0, ++ &sst_in_sw_pcm0), ++ SND_SOC_DAPM_SWITCH("In PCM1 Switch", SND_SOC_NOPM, 0, 0, ++ &sst_in_sw_pcm1), ++ SND_SOC_DAPM_SWITCH("In Media0 Switch", SND_SOC_NOPM, 0, 0, ++ &sst_in_sw_media0), ++ SND_SOC_DAPM_SWITCH("In Media1 Switch", SND_SOC_NOPM, 0, 0, ++ &sst_in_sw_media1), ++ SND_SOC_DAPM_SWITCH("In Media2 Switch", SND_SOC_NOPM, 0, 0, ++ &sst_in_sw_media2), ++ SND_SOC_DAPM_SWITCH("In FM Switch", SND_SOC_NOPM, 0, 0, ++ &sst_in_sw_fm), ++}; ++ ++static const struct snd_soc_dapm_route intercon[] = { ++ /* media mixer settings */ ++ { "In Media0 Switch", "Switch", "Media IN0"}, ++ { "In Media1 Switch", "Switch", "Media IN1"}, ++ { "MIX Media0", "Media0", "In Media0 Switch"}, ++ { "MIX Media0", "Media1", "In Media1 Switch"}, ++ { "MIX Media0", "Media2", "In Media2 Switch"}, ++ { "MIX Media1", "Media0", "In Media0 Switch"}, ++ { "MIX Media1", "Media1", "In Media1 Switch"}, ++ { "MIX Media1", "Media2", "In Media2 Switch"}, ++ ++ /* media to main mixer intercon */ ++ /* two media paths from media to main */ ++ { "Mix Media0 Switch", "Switch", "MIX Media0"}, ++ { "Out Media0 Switch", "Switch", "Mix Media0 Switch"}, ++ { "In PCM0 Switch", "Switch", "Out Media0 Switch"}, ++ { "Mix Media1 Switch", "Switch", "MIX Media1"}, ++ { "Out Media1 Switch", "Switch", "Mix Media1 Switch"}, ++ { "In PCM1 Switch", "Switch", "Out Media1 Switch"}, ++ /* one back from main to media */ ++ { "Mix PCM0 Switch", "Switch", "MIX PCM0"}, ++ { "Out PCM0 Switch", "Switch", "Mix PCM0 Switch"}, ++ { "In Media2 Switch", "Switch", "Out PCM0 Switch"}, ++ ++ /* main mixer inputs - all inputs connect to mixer */ ++ { "MIX Modem", "Modem", "In Modem Switch"}, ++ { "MIX Modem", "Codec0", "In Codec0 Switch"}, ++ { "MIX Modem", "Codec1", "In Codec1 Switch"}, ++ { "MIX Modem", "Speech_Tx", "In Speech Tx Switch"}, ++ { "MIX Modem", "Speech_Rx", "In Speech Rx Switch"}, ++ { "MIX Modem", "Tone", "In Tone Switch"}, ++ { "MIX Modem", "Voip", "In Voip Switch"}, ++ { "MIX Modem", "PCM0", "In PCM0 Switch"}, ++ { "MIX Modem", "PCM1", "In PCM1 Switch"}, ++ { "MIX Modem", "FM", "In FM Switch"}, ++ /* loops have output switches coming back to mixers */ ++ { "MIX Modem", "Sprot_L0", "Mix Sprot L0 Switch"}, ++ { "MIX Modem", "Media_L1", "Mix Media L1 Switch"}, ++ { "MIX Modem", "Media_L2", "Mix Media L2 Switch"}, ++ /* sidetone comes from speech out */ ++ { "MIX Modem", "Sidetone", "Mix Speech Tx Switch"}, ++ ++ { "MIX Codec0", "Modem", "In Modem Switch"}, ++ { "MIX Codec0", "Codec0", "In Codec0 Switch"}, ++ { "MIX Codec0", "Codec1", "In Codec1 Switch"}, ++ { "MIX Codec0", "Speech_Tx", "In Speech Tx Switch"}, ++ { "MIX Codec0", "Speech_Rx", "In Speech Rx Switch"}, ++ { "MIX Codec0", "Tone", "In Tone Switch"}, ++ { "MIX Codec0", "Voip", "In Voip Switch"}, ++ { "MIX Codec0", "PCM0", "In PCM0 Switch"}, ++ { "MIX Codec0", "PCM1", "In PCM1 Switch"}, ++ { "MIX Codec0", "FM", "In FM Switch"}, ++ /* loops have output switches coming back to mixers */ ++ { "MIX Codec0", "Sprot_L0", "Mix Sprot L0 Switch"}, ++ { "MIX Codec0", "Media_L1", "Mix Media L1 Switch"}, ++ { "MIX Codec0", "Media_L2", "Mix Media L2 Switch"}, ++ /* sidetone comes from speech out */ ++ { "MIX Codec0", "Sidetone", "Mix Speech Tx Switch"}, ++ ++ { "MIX Codec1", "Modem", "In Modem Switch"}, ++ { "MIX Codec1", "Codec0", "In Codec0 Switch"}, ++ { "MIX Codec1", "Codec1", "In Codec1 Switch"}, ++ { "MIX Codec1", "Speech_Tx", "In Speech Tx Switch"}, ++ { "MIX Codec1", "Speech_Rx", "In Speech Rx Switch"}, ++ { "MIX Codec1", "Tone", "In Tone Switch"}, ++ { "MIX Codec1", "Voip", "In Voip Switch"}, ++ { "MIX Codec1", "PCM0", "In PCM0 Switch"}, ++ { "MIX Codec1", "PCM1", "In PCM1 Switch"}, ++ { "MIX Codec1", "FM", "In FM Switch"}, ++ /* loops have output switches coming back to mixers */ ++ { "MIX Codec1", "Sprot_L0", "Mix Sprot L0 Switch"}, ++ { "MIX Codec1", "Media_L1", "Mix Media L1 Switch"}, ++ { "MIX Codec1", "Media_L2", "Mix Media L2 Switch"}, ++ /* sidetone comes from speech out */ ++ { "MIX Codec1", "Sidetone", "Mix Speech Tx Switch"}, ++ ++ { "MIX Sprot L0", "Modem", "In Modem Switch"}, ++ { "MIX Sprot L0", "Codec0", "In Codec0 Switch"}, ++ { "MIX Sprot L0", "Codec1", "In Codec1 Switch"}, ++ { "MIX Sprot L0", "Speech_Tx", "In Speech Tx Switch"}, ++ { "MIX Sprot L0", "Speech_Rx", "In Speech Rx Switch"}, ++ { "MIX Sprot L0", "Tone", "In Tone Switch"}, ++ { "MIX Sprot L0", "Voip", "In Voip Switch"}, ++ { "MIX Sprot L0", "PCM0", "In PCM0 Switch"}, ++ { "MIX Sprot L0", "PCM1", "In PCM1 Switch"}, ++ { "MIX Sprot L0", "FM", "In FM Switch"}, ++ /* loops have output switches coming back to mixers */ ++ { "MIX Sprot L0", "Sprot_L0", "Mix Sprot L0 Switch"}, ++ { "MIX Sprot L0", "Media_L1", "Mix Media L1 Switch"}, ++ { "MIX Sprot L0", "Media_L2", "Mix Media L2 Switch"}, ++ /* sidetone comes from speech out */ ++ { "MIX Sprot L0", "Sidetone", "Mix Speech Tx Switch"}, ++ ++ { "MIX Media L1", "Modem", "In Modem Switch"}, ++ { "MIX Media L1", "Codec0", "In Codec0 Switch"}, ++ { "MIX Media L1", "Codec1", "In Codec1 Switch"}, ++ { "MIX Media L1", "Speech_Tx", "In Speech Tx Switch"}, ++ { "MIX Media L1", "Speech_Rx", "In Speech Rx Switch"}, ++ { "MIX Media L1", "Tone", "In Tone Switch"}, ++ { "MIX Media L1", "Voip", "In Voip Switch"}, ++ { "MIX Media L1", "PCM0", "In PCM0 Switch"}, ++ { "MIX Media L1", "PCM1", "In PCM1 Switch"}, ++ { "MIX Media L1", "FM", "In FM Switch"}, ++ /* loops have output switches coming back to mixers */ ++ { "MIX Media L1", "Sprot_L0", "Mix Sprot L0 Switch"}, ++ { "MIX Media L1", "Media_L1", "Mix Media L1 Switch"}, ++ { "MIX Media L1", "Media_L2", "Mix Media L2 Switch"}, ++ /* sidetone comes from speech out */ ++ { "MIX Media L1", "Sidetone", "Mix Speech Tx Switch"}, ++ ++ { "MIX Media L2", "Modem", "In Modem Switch"}, ++ { "MIX Media L2", "Codec0", "In Codec0 Switch"}, ++ { "MIX Media L2", "Codec1", "In Codec1 Switch"}, ++ { "MIX Media L2", "Speech_Tx", "In Speech Tx Switch"}, ++ { "MIX Media L2", "Speech_Rx", "In Speech Rx Switch"}, ++ { "MIX Media L2", "Tone", "In Tone Switch"}, ++ { "MIX Media L2", "Voip", "In Voip Switch"}, ++ { "MIX Media L2", "PCM0", "In PCM0 Switch"}, ++ { "MIX Media L2", "PCM1", "In PCM1 Switch"}, ++ { "MIX Media L2", "FM", "In FM Switch"}, ++ /* loops have output switches coming back to mixers */ ++ { "MIX Media L2", "Sprot_L0", "Mix Sprot L0 Switch"}, ++ { "MIX Media L2", "Media_L1", "Mix Media L1 Switch"}, ++ { "MIX Media L2", "Media_L2", "Mix Media L2 Switch"}, ++ /* sidetone comes from speech out */ ++ { "MIX Media L2", "Sidetone", "Mix Speech Tx Switch"}, ++ ++ { "MIX Speech Rx", "Modem", "In Modem Switch"}, ++ { "MIX Speech Rx", "Codec0", "In Codec0 Switch"}, ++ { "MIX Speech Rx", "Codec1", "In Codec1 Switch"}, ++ { "MIX Speech Rx", "Speech_Tx", "In Speech Tx Switch"}, ++ { "MIX Speech Rx", "Speech_Rx", "In Speech Rx Switch"}, ++ { "MIX Speech Rx", "Tone", "In Tone Switch"}, ++ { "MIX Speech Rx", "Voip", "In Voip Switch"}, ++ { "MIX Speech Rx", "PCM0", "In PCM0 Switch"}, ++ { "MIX Speech Rx", "PCM1", "In PCM1 Switch"}, ++ { "MIX Speech Rx", "FM", "In FM Switch"}, ++ /* loops have output switches coming back to mixers */ ++ { "MIX Speech Rx", "Sprot_L0", "Mix Sprot L0 Switch"}, ++ { "MIX Speech Rx", "Media_L1", "Mix Media L1 Switch"}, ++ { "MIX Speech Rx", "Media_L2", "Mix Media L2 Switch"}, ++ /* sidetone comes from speech out */ ++ { "MIX Speech Rx", "Sidetone", "Mix Speech Tx Switch"}, ++ ++ { "MIX Speech Tx", "Modem", "In Modem Switch"}, ++ { "MIX Speech Tx", "Codec0", "In Codec0 Switch"}, ++ { "MIX Speech Tx", "Codec1", "In Codec1 Switch"}, ++ { "MIX Speech Tx", "Speech_Tx", "In Speech Tx Switch"}, ++ { "MIX Speech Tx", "Speech_Rx", "In Speech Rx Switch"}, ++ { "MIX Speech Tx", "Tone", "In Tone Switch"}, ++ { "MIX Speech Tx", "Voip", "In Voip Switch"}, ++ { "MIX Speech Tx", "PCM0", "In PCM0 Switch"}, ++ { "MIX Speech Tx", "PCM1", "In PCM1 Switch"}, ++ { "MIX Speech Tx", "FM", "In FM Switch"}, ++ /* loops have output switches coming back to mixers */ ++ { "MIX Speech Tx", "Sprot_L0", "Mix Sprot L0 Switch"}, ++ { "MIX Speech Tx", "Media_L1", "Mix Media L1 Switch"}, ++ { "MIX Speech Tx", "Media_L2", "Mix Media L2 Switch"}, ++ /* sidetone comes from speech out */ ++ { "MIX Speech Tx", "Sidetone", "Mix Speech Tx Switch"}, ++ ++ { "MIX Voip", "Modem", "In Modem Switch"}, ++ { "MIX Voip", "Codec0", "In Codec0 Switch"}, ++ { "MIX Voip", "Codec1", "In Codec1 Switch"}, ++ { "MIX Voip", "Speech_Tx", "In Speech Tx Switch"}, ++ { "MIX Voip", "Speech_Rx", "In Speech Rx Switch"}, ++ { "MIX Voip", "Tone", "In Tone Switch"}, ++ { "MIX Voip", "Voip", "In Voip Switch"}, ++ { "MIX Voip", "PCM0", "In PCM0 Switch"}, ++ { "MIX Voip", "PCM1", "In PCM1 Switch"}, ++ { "MIX Voip", "FM", "In FM Switch"}, ++ /* loops have output switches coming back to mixers */ ++ { "MIX Voip", "Sprot_L0", "Mix Sprot L0 Switch"}, ++ { "MIX Voip", "Media_L1", "Mix Media L1 Switch"}, ++ { "MIX Voip", "Media_L2", "Mix Media L2 Switch"}, ++ /* sidetone comes from speech out */ ++ { "MIX Voip", "Sidetone", "Mix Speech Tx Switch"}, ++ ++ { "MIX PCM0", "Modem", "In Modem Switch"}, ++ { "MIX PCM0", "Codec0", "In Codec0 Switch"}, ++ { "MIX PCM0", "Codec1", "In Codec1 Switch"}, ++ { "MIX PCM0", "Speech_Tx", "In Speech Tx Switch"}, ++ { "MIX PCM0", "Speech_Rx", "In Speech Rx Switch"}, ++ { "MIX PCM0", "Tone", "In Tone Switch"}, ++ { "MIX PCM0", "Voip", "In Voip Switch"}, ++ { "MIX PCM0", "PCM0", "In PCM0 Switch"}, ++ { "MIX PCM0", "PCM1", "In PCM1 Switch"}, ++ { "MIX PCM0", "FM", "In FM Switch"}, ++ /* loops have output switches coming back to mixers */ ++ { "MIX PCM0", "Sprot_L0", "Mix Sprot L0 Switch"}, ++ { "MIX PCM0", "Media_L1", "Mix Media L1 Switch"}, ++ { "MIX PCM0", "Media_L2", "Mix Media L2 Switch"}, ++ /* sidetone comes from speech out */ ++ { "MIX PCM0", "Sidetone", "Mix Speech Tx Switch"}, ++ ++ { "MIX PCM1", "Modem", "In Modem Switch"}, ++ { "MIX PCM1", "Codec0", "In Codec0 Switch"}, ++ { "MIX PCM1", "Codec1", "In Codec1 Switch"}, ++ { "MIX PCM1", "Speech_Tx", "In Speech Tx Switch"}, ++ { "MIX PCM1", "Speech_Rx", "In Speech Rx Switch"}, ++ { "MIX PCM1", "Tone", "In Tone Switch"}, ++ { "MIX PCM1", "Voip", "In Voip Switch"}, ++ { "MIX PCM1", "PCM0", "In PCM0 Switch"}, ++ { "MIX PCM1", "PCM1", "In PCM1 Switch"}, ++ { "MIX PCM1", "FM", "In FM Switch"}, ++ /* loops have output switches coming back to mixers */ ++ { "MIX PCM1", "Sprot_L0", "Mix Sprot L0 Switch"}, ++ { "MIX PCM1", "Media_L1", "Mix Media L1 Switch"}, ++ { "MIX PCM1", "Media_L2", "Mix Media L2 Switch"}, ++ /* sidetone comes from speech out */ ++ { "MIX PCM1", "Sidetone", "Mix Speech Tx Switch"}, ++ ++ { "MIX PCM2", "Modem", "In Modem Switch"}, ++ { "MIX PCM2", "Codec0", "In Codec0 Switch"}, ++ { "MIX PCM2", "Codec1", "In Codec1 Switch"}, ++ { "MIX PCM2", "Speech_Tx", "In Speech Tx Switch"}, ++ { "MIX PCM2", "Speech_Rx", "In Speech Rx Switch"}, ++ { "MIX PCM2", "Tone", "In Tone Switch"}, ++ { "MIX PCM2", "Voip", "In Voip Switch"}, ++ { "MIX PCM2", "PCM0", "In PCM0 Switch"}, ++ { "MIX PCM2", "PCM1", "In PCM1 Switch"}, ++ { "MIX PCM2", "FM", "In FM Switch"}, ++ /* loops have output switches coming back to mixers */ ++ { "MIX PCM2", "Sprot_L0", "Mix Sprot L0 Switch"}, ++ { "MIX PCM2", "Media_L1", "Mix Media L1 Switch"}, ++ { "MIX PCM2", "Media_L2", "Mix Media L2 Switch"}, ++ /* sidetone comes from speech out */ ++ { "MIX PCM2", "Sidetone", "Mix Speech Tx Switch"}, ++ ++ { "MIX Aware", "Modem", "In Modem Switch"}, ++ { "MIX Aware", "Codec0", "In Codec0 Switch"}, ++ { "MIX Aware", "Codec1", "In Codec1 Switch"}, ++ { "MIX Aware", "Speech_Tx", "In Speech Tx Switch"}, ++ { "MIX Aware", "Speech_Rx", "In Speech Rx Switch"}, ++ { "MIX Aware", "Tone", "In Tone Switch"}, ++ { "MIX Aware", "Voip", "In Voip Switch"}, ++ { "MIX Aware", "PCM0", "In PCM0 Switch"}, ++ { "MIX Aware", "PCM1", "In PCM1 Switch"}, ++ { "MIX Aware", "FM", "In FM Switch"}, ++ /* loops have output switches coming back to mixers */ ++ { "MIX Aware", "Sprot_L0", "Mix Sprot L0 Switch"}, ++ { "MIX Aware", "Media_L1", "Mix Media L1 Switch"}, ++ { "MIX Aware", "Media_L2", "Mix Media L2 Switch"}, ++ /* sidetone comes from speech out */ ++ { "MIX Aware", "Sidetone", "Mix Speech Tx Switch"}, ++ ++ { "MIX VAD", "Modem", "In Modem Switch"}, ++ { "MIX VAD", "Codec0", "In Codec0 Switch"}, ++ { "MIX VAD", "Codec1", "In Codec1 Switch"}, ++ { "MIX VAD", "Speech_Tx", "In Speech Tx Switch"}, ++ { "MIX VAD", "Speech_Rx", "In Speech Rx Switch"}, ++ { "MIX VAD", "Tone", "In Tone Switch"}, ++ { "MIX VAD", "Voip", "In Voip Switch"}, ++ { "MIX VAD", "PCM0", "In PCM0 Switch"}, ++ { "MIX VAD", "PCM1", "In PCM1 Switch"}, ++ { "MIX VAD", "FM", "In FM Switch"}, ++ /* loops have output switches coming back to mixers */ ++ { "MIX VAD", "Sprot_L0", "Mix Sprot L0 Switch"}, ++ { "MIX VAD", "Media_L1", "Mix Media L1 Switch"}, ++ { "MIX VAD", "Media_L2", "Mix Media L2 Switch"}, ++ /* sidetone comes from speech out */ ++ { "MIX VAD", "Sidetone", "Mix Speech Tx Switch"}, ++ ++ { "MIX FM", "Modem", "In Modem Switch"}, ++ { "MIX FM", "Codec0", "In Codec0 Switch"}, ++ { "MIX FM", "Codec1", "In Codec1 Switch"}, ++ { "MIX FM", "Speech_Tx", "In Speech Tx Switch"}, ++ { "MIX FM", "Speech_Rx", "In Speech Rx Switch"}, ++ { "MIX FM", "Tone", "In Tone Switch"}, ++ { "MIX FM", "Voip", "In Voip Switch"}, ++ { "MIX FM", "PCM0", "In PCM0 Switch"}, ++ { "MIX FM", "PCM1", "In PCM1 Switch"}, ++ { "MIX FM", "FM", "In FM Switch"}, ++ /* loops have output switches coming back to mixers */ ++ { "MIX FM", "Sprot_L0", "Mix Sprot L0 Switch"}, ++ { "MIX FM", "Media_L1", "Mix Media L1 Switch"}, ++ { "MIX FM", "Media_L2", "Mix Media L2 Switch"}, ++ /* sidetone comes from speech out */ ++ { "MIX FM", "Sidetone", "Mix Speech Tx Switch"}, ++ ++ /* now connect the mixers to output switches */ ++ { "Mix Modem Switch", "Switch", "MIX Modem"}, ++ { "Out Modem Switch", "Switch", "Mix Modem Switch"}, ++ { "Mix Codec0 Switch", "Switch", "MIX Codec0"}, ++ { "Out Codec0 Switch", "Switch", "Mix Codec0 Switch"}, ++ { "Mix Codec1 Switch", "Switch", "MIX Codec1"}, ++ { "Out Codec1 Switch", "Switch", "Mix Codec1 Switch"}, ++ { "Mix Speech Tx Switch", "Switch", "MIX Speech Tx"}, ++ { "Out Speech Tx Switch", "Switch", "Mix Speech Tx Switch"}, ++ { "Mix Speech Rx Switch", "Switch", "MIX Speech Rx"}, ++ { "Out Speech Rx Switch", "Switch", "Mix Speech Rx Switch"}, ++ { "Mix Voip Switch", "Switch", "MIX Voip"}, ++ { "Out Voip Switch", "Switch", "Mix Voip Switch"}, ++ { "Mix Aware Switch", "Switch", "MIX Aware"}, ++ { "Out Aware Switch", "Switch", "Mix Aware Switch"}, ++ { "Mix VAD Switch", "Switch", "MIX VAD"}, ++ { "Out VAD Switch", "Switch", "Mix VAD Switch"}, ++ { "Mix FM Switch", "Switch", "MIX FM"}, ++ { "Out FM Switch", "Switch", "Mix FM Switch"}, ++ { "Mix PCM1 Switch", "Switch", "MIX PCM1"}, ++ { "Out PCM1 Switch", "Switch", "Mix PCM1 Switch"}, ++ { "Mix PCM2 Switch", "Switch", "MIX PCM2"}, ++ { "Out PCM2 Switch", "Switch", "Mix PCM2 Switch"}, ++ ++ /* the loops ++ * media loops dont have i/p o/p switches, just mixer enable ++ */ ++ { "Mix Sprot L0 Switch", "Switch", "MIX Sprot L0"}, ++ { "Mix Media L1 Switch", "Switch", "MIX Media L1"}, ++ { "Mix Media L2 Switch", "Switch", "MIX Media L2"}, ++ /* so no need as mixer switches are ++ * inputs to all mixers ++ * need to connect speech loops here ++ */ ++ { "In Speech Rx Switch", "Switch", "Out Speech Rx Switch"}, ++ { "In Speech Tx Switch", "Switch", "Out Speech Tx Switch"}, ++ /* last one, connect the output switches to ip's ++ * and op's. Also connect the AIFs ++ */ ++ { "In Modem Switch", "Switch", "Modem IN"}, ++ { "In Codec0 Switch", "Switch", "Codec IN0"}, ++ { "In Codec1 Switch", "Switch", "Codec IN1"}, ++ { "In Tone Switch", "Switch", "Tone IN"}, ++ { "In FM Switch", "Switch", "FM IN"}, ++ ++ { "Modem OUT", NULL, "Out Modem Switch"}, ++ { "Codec OUT0", NULL, "Out Codec0 Switch"}, ++ { "Codec OUT1", NULL, "Out Codec1 Switch"}, ++ { "FM OUT", NULL, "Out FM Switch"}, ++ ++ { "In Voip Switch", "Switch", "Voip IN"}, ++ ++ { "Voip OUT", NULL, "Out Voip Switch"}, ++ { "PCM1 OUT", NULL, "Out PCM1 Switch"}, ++ { "Aware OUT", NULL, "Out Aware Switch"}, ++ { "VAD OUT", NULL, "Out VAD Switch"}, ++}; ++ ++int sst_byte_control_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); ++ struct sst_data *sst = snd_soc_platform_get_drvdata(platform); ++ ++ pr_debug("in %s\n", __func__); ++ memcpy(ucontrol->value.bytes.data, sst->byte_stream, SST_MAX_BIN_BYTES); ++ print_hex_dump_bytes(__func__, DUMP_PREFIX_OFFSET, ++ (const void *)sst->byte_stream, 32); ++ return 0; ++} ++ ++static int sst_check_binary_input(char *stream) ++{ ++ struct snd_sst_bytes_v2 *bytes = (struct snd_sst_bytes_v2 *)stream; ++ ++ if (bytes->len == 0 || bytes->len > 1000) { ++ pr_err("length out of bounds %d\n", bytes->len); ++ return -EINVAL; ++ } ++ if (bytes->type == 0 || bytes->type > SND_SST_BYTES_GET) { ++ pr_err("type out of bounds: %d\n", bytes->type); ++ return -EINVAL; ++ } ++ if (bytes->block > 1) { ++ pr_err("block invalid %d\n", bytes->block); ++ return -EINVAL; ++ } ++ if (bytes->task_id == SST_TASK_ID_NONE || bytes->task_id > SST_TASK_ID_MAX) { ++ pr_err("taskid invalid %d\n", bytes->task_id); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++int sst_byte_control_set(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); ++ struct sst_data *sst = snd_soc_platform_get_drvdata(platform); ++ int ret = 0; ++ ++ pr_debug("in %s\n", __func__); ++ mutex_lock(&sst->lock); ++ memcpy(sst->byte_stream, ucontrol->value.bytes.data, SST_MAX_BIN_BYTES); ++ if (0 != sst_check_binary_input(sst->byte_stream)) { ++ mutex_unlock(&sst->lock); ++ return -EINVAL; ++ } ++ print_hex_dump_bytes(__func__, DUMP_PREFIX_OFFSET, ++ (const void *)sst->byte_stream, 32); ++ ret = sst_dsp->ops->set_generic_params(SST_SET_BYTE_STREAM, sst->byte_stream); ++ mutex_unlock(&sst->lock); ++ ++ return ret; ++} ++ ++static int sst_pipe_id_control_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); ++ struct sst_data *sst = snd_soc_platform_get_drvdata(platform); ++ int ret = 0; ++ ++ ucontrol->value.integer.value[0] = sst->pipe_id; ++ ++ return ret; ++} ++ ++static int sst_pipe_id_control_set(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); ++ struct sst_data *sst = snd_soc_platform_get_drvdata(platform); ++ int ret = 0; ++ ++ sst->pipe_id = ucontrol->value.integer.value[0]; ++ pr_debug("%s: pipe_id %d", __func__, sst->pipe_id); ++ ++ return ret; ++} ++ ++/* dB range for mrfld compress volume is -144dB to +36dB. ++ * Gain library expects user input in terms of 0.1dB, for example, ++ * 60 (in decimal) represents 6dB. ++ * MW will pass 2's complement value for negative dB values. ++ */ ++static int sst_compr_vol_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); ++ struct sst_data *sst = snd_soc_platform_get_drvdata(platform); ++ struct sst_algo_int_control_v2 *amc = (void *)kcontrol->private_value; ++ u16 gain; ++ unsigned int gain_offset, ret; ++ ++ sst_create_compr_vol_ipc(sst->byte_stream, SND_SST_BYTES_GET, amc); ++ mutex_lock(&sst->lock); ++ ret = sst_dsp->ops->set_generic_params(SST_SET_BYTE_STREAM, ++ sst->byte_stream); ++ mutex_unlock(&sst->lock); ++ if (ret) { ++ pr_err("failed to get compress vol from fw: %d\n", ret); ++ return ret; ++ } ++ gain_offset = sizeof(struct snd_sst_bytes_v2) + ++ sizeof(struct ipc_dsp_hdr); ++ ++ /* Get params format for vol ctrl lib, size 6 bytes : ++ * u16 left_gain, u16 right_gain, u16 ramp ++ */ ++ memcpy(&gain, ++ (unsigned int *)(sst->byte_stream + gain_offset), ++ sizeof(u16)); ++ pr_debug("%s: cell_gain = %d\n", __func__, gain); ++ amc->value = gain; ++ ucontrol->value.integer.value[0] = gain; ++ return 0; ++} ++ ++static int sst_compr_vol_set(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); ++ struct sst_data *sst = snd_soc_platform_get_drvdata(platform); ++ struct sst_algo_int_control_v2 *amc = (void *)kcontrol->private_value; ++ int ret = 0; ++ unsigned int old_val; ++ ++ pr_debug("%s: cell_gain = %ld\n", __func__,\ ++ ucontrol->value.integer.value[0]); ++ old_val = amc->value; ++ amc->value = ucontrol->value.integer.value[0]; ++ sst_create_compr_vol_ipc(sst->byte_stream, SND_SST_BYTES_SET, ++ amc); ++ ++ mutex_lock(&sst->lock); ++ ret = sst_dsp->ops->set_generic_params(SST_SET_BYTE_STREAM, ++ sst->byte_stream); ++ mutex_unlock(&sst->lock); ++ if (ret) { ++ pr_err("failed to set compress vol in fw: %d\n", ret); ++ amc->value = old_val; ++ return ret; ++ } ++ return 0; ++} ++ ++static int sst_vtsv_enroll_set(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); ++ struct sst_data *sst = snd_soc_platform_get_drvdata(platform); ++ int ret = 0; ++ ++ sst->vtsv_enroll = ucontrol->value.integer.value[0]; ++ mutex_lock(&sst->lock); ++ if (sst->vtsv_enroll) ++ ret = sst_dsp->ops->set_generic_params(SST_SET_VTSV_INFO, ++ (void *)&sst->vtsv_enroll); ++ mutex_unlock(&sst->lock); ++ return ret; ++} ++ ++static int sst_vtsv_enroll_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); ++ struct sst_data *sst = snd_soc_platform_get_drvdata(platform); ++ ++ ucontrol->value.integer.value[0] = sst->vtsv_enroll; ++ return 0; ++} ++ ++/* This value corresponds to two's complement value of -10 or -1dB */ ++#define SST_COMPR_VOL_MAX_INTEG_GAIN 0xFFF6 ++#define SST_COMPR_VOL_MUTE 0xFA60 /* 2's complement of -1440 or -144dB*/ ++ ++ ++static const struct snd_kcontrol_new sst_mrfld_controls[] = { ++ SND_SOC_BYTES_EXT("SST Byte control", SST_MAX_BIN_BYTES, ++ sst_byte_control_get, sst_byte_control_set), ++ SOC_SINGLE_EXT("SST Pipe_id control", SST_PIPE_CONTROL, 0, 0x9A, 0, ++ sst_pipe_id_control_get, sst_pipe_id_control_set), ++ SST_ALGO_KCONTROL_INT("Compress Volume", SST_COMPRESS_VOL, ++ 0, SST_COMPR_VOL_MAX_INTEG_GAIN, 0, ++ sst_compr_vol_get, sst_compr_vol_set, ++ SST_ALGO_VOLUME_CONTROL, PIPE_MEDIA0_IN, 0, ++ SST_COMPR_VOL_MUTE), ++ SOC_SINGLE_BOOL_EXT("SST VTSV Enroll", 0, sst_vtsv_enroll_get, ++ sst_vtsv_enroll_set), ++}; ++ ++static DEVICE_ULONG_ATTR(low_latency_threshold, 0644, ll_threshold); ++static DEVICE_ULONG_ATTR(deep_buffer_threshold, 0644, db_threshold); ++ ++static struct attribute *device_sysfs_attrs[] = { ++ &dev_attr_low_latency_threshold.attr.attr, ++ &dev_attr_deep_buffer_threshold.attr.attr, ++ NULL, ++}; ++ ++static struct attribute_group attr_group = { ++ .attrs = device_sysfs_attrs, ++}; ++ ++int sst_dsp_init(struct snd_soc_platform *platform) ++{ ++ struct sst_data *sst = snd_soc_platform_get_drvdata(platform); ++ int error = 0; ++ ++ sst->byte_stream = devm_kzalloc(platform->dev, ++ SST_MAX_BIN_BYTES, GFP_KERNEL); ++ if (sst->byte_stream == NULL) { ++ pr_err("kzalloc failed\n"); ++ return -ENOMEM; ++ } ++ ++ sst->widget = devm_kzalloc(platform->dev, ++ SST_NUM_WIDGETS * sizeof(*sst->widget), ++ GFP_KERNEL); ++ if (sst->widget == NULL) { ++ pr_err("kzalloc failed\n"); ++ return -ENOMEM; ++ } ++ ++ sst->vtsv_enroll = false; ++ /* Assign the pointer variables */ ++ sst->ll_db.low_latency = &ll_threshold; ++ sst->ll_db.deep_buffer = &db_threshold; ++ ++ pr_debug("Default ll thres %lu db thres %lu\n", ll_threshold, db_threshold); ++ ++ snd_soc_dapm_new_controls(&platform->dapm, sst_dapm_widgets, ++ ARRAY_SIZE(sst_dapm_widgets)); ++ snd_soc_dapm_add_routes(&platform->dapm, intercon, ++ ARRAY_SIZE(intercon)); ++ snd_soc_dapm_new_widgets(&platform->dapm); ++ snd_soc_add_platform_controls(platform, sst_mrfld_controls, ++ ARRAY_SIZE(sst_mrfld_controls)); ++ ++ error = sysfs_create_group(&platform->dev->kobj, &attr_group); ++ if (error) ++ pr_err("failed to create sysfs files %d\n", error); ++ ++ return error; ++} +diff --git a/sound/soc/intel/platform-libs/controls_v2.h b/sound/soc/intel/platform-libs/controls_v2.h +new file mode 100644 +index 0000000..b54c947 +--- /dev/null ++++ b/sound/soc/intel/platform-libs/controls_v2.h +@@ -0,0 +1,708 @@ ++/* ++ * controls_v2.h - Intel MID Platform driver header file ++ * ++ * Copyright (C) 2013 Intel Corp ++ * Author: Ramesh Babu ++ * Author: Omair M Abdullah ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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_CONTROLS_V2_H__ ++#define __SST_CONTROLS_V2_H__ ++ ++/* ++ * This section defines the map for the mixer widgets. ++ * ++ * Each mixer will be represented by single value and that value will have each ++ * bit corresponding to one input ++ * ++ * Each out_id will correspond to one mixer and one path. Each input will be ++ * represented by single bit in the register. ++ */ ++ ++/* mixer register ids here */ ++#define SST_MIX(x) (x) ++ ++#define SST_MIX_MODEM SST_MIX(0) ++#define SST_MIX_BT SST_MIX(1) ++#define SST_MIX_CODEC0 SST_MIX(2) ++#define SST_MIX_CODEC1 SST_MIX(3) ++#define SST_MIX_LOOP0 SST_MIX(4) ++#define SST_MIX_LOOP1 SST_MIX(5) ++#define SST_MIX_LOOP2 SST_MIX(6) ++#define SST_MIX_PROBE SST_MIX(7) ++#define SST_MIX_HF_SNS SST_MIX(8) ++#define SST_MIX_HF SST_MIX(9) ++#define SST_MIX_SPEECH SST_MIX(10) ++#define SST_MIX_RXSPEECH SST_MIX(11) ++#define SST_MIX_VOIP SST_MIX(12) ++#define SST_MIX_PCM0 SST_MIX(13) ++#define SST_MIX_PCM1 SST_MIX(14) ++#define SST_MIX_PCM2 SST_MIX(15) ++#define SST_MIX_AWARE SST_MIX(16) ++#define SST_MIX_VAD SST_MIX(17) ++#define SST_MIX_FM SST_MIX(18) ++ ++#define SST_MIX_MEDIA0 SST_MIX(19) ++#define SST_MIX_MEDIA1 SST_MIX(20) ++ ++#define SST_NUM_MIX (SST_MIX_MEDIA1 + 1) ++ ++#define SST_MIX_SWITCH (SST_NUM_MIX + 1) ++#define SST_OUT_SWITCH (SST_NUM_MIX + 2) ++#define SST_IN_SWITCH (SST_NUM_MIX + 3) ++#define SST_MUX_REG (SST_NUM_MIX + 4) ++#define SST_REG_LAST (SST_MUX_REG) ++ ++/* last entry defines array size */ ++#define SST_NUM_WIDGETS (SST_REG_LAST + 1) ++ ++#define SST_BT_FM_MUX_SHIFT 0 ++#define SST_VOICE_MODE_SHIFT 1 ++#define SST_BT_MODE_SHIFT 2 ++ ++/* in each mixer register we will define one bit for each input */ ++#define SST_MIX_IP(x) (x) ++ ++#define SST_IP_MODEM SST_MIX_IP(0) ++#define SST_IP_BT SST_MIX_IP(1) ++#define SST_IP_CODEC0 SST_MIX_IP(2) ++#define SST_IP_CODEC1 SST_MIX_IP(3) ++#define SST_IP_LOOP0 SST_MIX_IP(4) ++#define SST_IP_LOOP1 SST_MIX_IP(5) ++#define SST_IP_LOOP2 SST_MIX_IP(6) ++#define SST_IP_PROBE SST_MIX_IP(7) ++#define SST_IP_SIDETONE SST_MIX_IP(8) ++#define SST_IP_TXSPEECH SST_MIX_IP(9) ++#define SST_IP_SPEECH SST_MIX_IP(10) ++#define SST_IP_TONE SST_MIX_IP(11) ++#define SST_IP_VOIP SST_MIX_IP(12) ++#define SST_IP_PCM0 SST_MIX_IP(13) ++#define SST_IP_PCM1 SST_MIX_IP(14) ++#define SST_IP_LOW_PCM0 SST_MIX_IP(15) ++#define SST_IP_FM SST_MIX_IP(16) ++#define SST_IP_MEDIA0 SST_MIX_IP(17) ++#define SST_IP_MEDIA1 SST_MIX_IP(18) ++#define SST_IP_MEDIA2 SST_MIX_IP(19) ++#define SST_IP_MEDIA3 SST_MIX_IP(20) ++ ++#define SST_IP_LAST SST_IP_MEDIA3 ++ ++#define SST_SWM_INPUT_COUNT (SST_IP_LAST + 1) ++#define SST_CMD_SWM_MAX_INPUTS 6 ++ ++#define SST_PATH_ID_SHIFT 8 ++#define SST_DEFAULT_LOCATION_ID 0xFFFF ++#define SST_DEFAULT_CELL_NBR 0xFF ++#define SST_DEFAULT_MODULE_ID 0xFFFF ++ ++/* ++ * Audio DSP Path Ids. Specified by the audio DSP FW ++ */ ++enum sst_path_index { ++ SST_PATH_INDEX_MODEM_OUT = (0x00 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_BT_OUT = (0x01 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_CODEC_OUT0 = (0x02 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_CODEC_OUT1 = (0x03 << SST_PATH_ID_SHIFT), ++ ++ SST_PATH_INDEX_SPROT_LOOP_OUT = (0x04 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_MEDIA_LOOP1_OUT = (0x05 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_MEDIA_LOOP2_OUT = (0x06 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_PROBE_OUT = (0x07 << SST_PATH_ID_SHIFT), ++ ++ SST_PATH_INDEX_HF_SNS_OUT = (0x08 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_VOICE_UPLINK_REF2 = (0x08 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_HF_OUT = (0x09 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_VOICE_UPLINK_REF1 = (0x09 << SST_PATH_ID_SHIFT), ++ ++ SST_PATH_INDEX_SPEECH_OUT = (0x0A << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_VOICE_UPLINK = (0x0A << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_RX_SPEECH_OUT = (0x0B << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_VOICE_DOWNLINK = (0x0B << SST_PATH_ID_SHIFT), ++ ++ SST_PATH_INDEX_VOIP_OUT = (0x0C << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_PCM0_OUT = (0x0D << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_PCM1_OUT = (0x0E << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_PCM2_OUT = (0x0F << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_AWARE_OUT = (0x10 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_VAD_OUT = (0x11 << SST_PATH_ID_SHIFT), ++ ++ SST_PATH_INDEX_MEDIA0_OUT = (0x12 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_MEDIA1_OUT = (0x13 << SST_PATH_ID_SHIFT), ++ ++ SST_PATH_INDEX_FM_OUT = (0x14 << SST_PATH_ID_SHIFT), ++ ++ SST_PATH_INDEX_PROBE1_PIPE_OUT = (0x15 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_PROBE2_PIPE_OUT = (0x16 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_PROBE3_PIPE_OUT = (0x17 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_PROBE4_PIPE_OUT = (0x18 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_PROBE5_PIPE_OUT = (0x19 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_PROBE6_PIPE_OUT = (0x1A << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_PROBE7_PIPE_OUT = (0x1B << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_PROBE8_PIPE_OUT = (0x1C << SST_PATH_ID_SHIFT), ++ ++ SST_PATH_INDEX_SIDETONE_OUT = (0x1D << SST_PATH_ID_SHIFT), ++ ++ /* Start of input paths */ ++ SST_PATH_INDEX_MODEM_IN = (0x80 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_BT_IN = (0x81 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_CODEC_IN0 = (0x82 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_CODEC_IN1 = (0x83 << SST_PATH_ID_SHIFT), ++ ++ SST_PATH_INDEX_SPROT_LOOP_IN = (0x84 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_MEDIA_LOOP1_IN = (0x85 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_MEDIA_LOOP2_IN = (0x86 << SST_PATH_ID_SHIFT), ++ ++ SST_PATH_INDEX_PROBE_IN = (0x87 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_SIDETONE_IN = (0x88 << SST_PATH_ID_SHIFT), ++ ++ SST_PATH_INDEX_TX_SPEECH_IN = (0x89 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_SPEECH_IN = (0x8A << SST_PATH_ID_SHIFT), ++ ++ SST_PATH_INDEX_TONE_IN = (0x8B << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_VOIP_IN = (0x8C << SST_PATH_ID_SHIFT), ++ ++ SST_PATH_INDEX_PCM0_IN = (0x8D << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_PCM1_IN = (0x8E << SST_PATH_ID_SHIFT), ++ ++ SST_PATH_INDEX_MEDIA0_IN = (0x8F << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_MEDIA1_IN = (0x90 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_MEDIA2_IN = (0x91 << SST_PATH_ID_SHIFT), ++ ++ SST_PATH_INDEX_FM_IN = (0x92 << SST_PATH_ID_SHIFT), ++ ++ SST_PATH_INDEX_PROBE1_PIPE_IN = (0x93 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_PROBE2_PIPE_IN = (0x94 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_PROBE3_PIPE_IN = (0x95 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_PROBE4_PIPE_IN = (0x96 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_PROBE5_PIPE_IN = (0x97 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_PROBE6_PIPE_IN = (0x98 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_PROBE7_PIPE_IN = (0x99 << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_PROBE8_PIPE_IN = (0x9A << SST_PATH_ID_SHIFT), ++ ++ SST_PATH_INDEX_MEDIA3_IN = (0x9C << SST_PATH_ID_SHIFT), ++ SST_PATH_INDEX_LOW_PCM0_IN = (0x9D << SST_PATH_ID_SHIFT), ++ ++ SST_PATH_INDEX_RESERVED = (0xFF << SST_PATH_ID_SHIFT), ++}; ++ ++/* ++ * switch matrix input path IDs ++ */ ++enum sst_swm_inputs { ++ SST_SWM_IN_MODEM = (SST_PATH_INDEX_MODEM_IN | SST_DEFAULT_CELL_NBR), ++ SST_SWM_IN_BT = (SST_PATH_INDEX_BT_IN | SST_DEFAULT_CELL_NBR), ++ SST_SWM_IN_CODEC0 = (SST_PATH_INDEX_CODEC_IN0 | SST_DEFAULT_CELL_NBR), ++ SST_SWM_IN_CODEC1 = (SST_PATH_INDEX_CODEC_IN1 | SST_DEFAULT_CELL_NBR), ++ SST_SWM_IN_SPROT_LOOP = (SST_PATH_INDEX_SPROT_LOOP_IN | SST_DEFAULT_CELL_NBR), ++ SST_SWM_IN_MEDIA_LOOP1 = (SST_PATH_INDEX_MEDIA_LOOP1_IN | SST_DEFAULT_CELL_NBR), ++ SST_SWM_IN_MEDIA_LOOP2 = (SST_PATH_INDEX_MEDIA_LOOP2_IN | SST_DEFAULT_CELL_NBR), ++ SST_SWM_IN_PROBE = (SST_PATH_INDEX_PROBE_IN | SST_DEFAULT_CELL_NBR), ++ SST_SWM_IN_SIDETONE = (SST_PATH_INDEX_SIDETONE_IN | SST_DEFAULT_CELL_NBR), ++ SST_SWM_IN_TXSPEECH = (SST_PATH_INDEX_TX_SPEECH_IN | SST_DEFAULT_CELL_NBR), ++ SST_SWM_IN_SPEECH = (SST_PATH_INDEX_SPEECH_IN | SST_DEFAULT_CELL_NBR), ++ SST_SWM_IN_TONE = (SST_PATH_INDEX_TONE_IN | SST_DEFAULT_CELL_NBR), ++ SST_SWM_IN_VOIP = (SST_PATH_INDEX_VOIP_IN | SST_DEFAULT_CELL_NBR), ++ SST_SWM_IN_PCM0 = (SST_PATH_INDEX_PCM0_IN | SST_DEFAULT_CELL_NBR), ++ SST_SWM_IN_PCM1 = (SST_PATH_INDEX_PCM1_IN | SST_DEFAULT_CELL_NBR), ++ SST_SWM_IN_MEDIA0 = (SST_PATH_INDEX_MEDIA0_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ ++ SST_SWM_IN_MEDIA1 = (SST_PATH_INDEX_MEDIA1_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ ++ SST_SWM_IN_MEDIA2 = (SST_PATH_INDEX_MEDIA2_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ ++ SST_SWM_IN_FM = (SST_PATH_INDEX_FM_IN | SST_DEFAULT_CELL_NBR), ++ SST_SWM_IN_MEDIA3 = (SST_PATH_INDEX_MEDIA3_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ ++ SST_SWM_IN_LOW_PCM0 = (SST_PATH_INDEX_LOW_PCM0_IN | SST_DEFAULT_CELL_NBR), ++ SST_SWM_IN_END = (SST_PATH_INDEX_RESERVED | SST_DEFAULT_CELL_NBR) ++}; ++ ++/* ++ * switch matrix output path IDs ++ */ ++enum sst_swm_outputs { ++ SST_SWM_OUT_MODEM = (SST_PATH_INDEX_MODEM_OUT | SST_DEFAULT_CELL_NBR), ++ SST_SWM_OUT_BT = (SST_PATH_INDEX_BT_OUT | SST_DEFAULT_CELL_NBR), ++ SST_SWM_OUT_CODEC0 = (SST_PATH_INDEX_CODEC_OUT0 | SST_DEFAULT_CELL_NBR), ++ SST_SWM_OUT_CODEC1 = (SST_PATH_INDEX_CODEC_OUT1 | SST_DEFAULT_CELL_NBR), ++ SST_SWM_OUT_SPROT_LOOP = (SST_PATH_INDEX_SPROT_LOOP_OUT | SST_DEFAULT_CELL_NBR), ++ SST_SWM_OUT_MEDIA_LOOP1 = (SST_PATH_INDEX_MEDIA_LOOP1_OUT | SST_DEFAULT_CELL_NBR), ++ SST_SWM_OUT_MEDIA_LOOP2 = (SST_PATH_INDEX_MEDIA_LOOP2_OUT | SST_DEFAULT_CELL_NBR), ++ SST_SWM_OUT_PROBE = (SST_PATH_INDEX_PROBE_OUT | SST_DEFAULT_CELL_NBR), ++ SST_SWM_OUT_HF_SNS = (SST_PATH_INDEX_HF_SNS_OUT | SST_DEFAULT_CELL_NBR), ++ SST_SWM_OUT_HF = (SST_PATH_INDEX_HF_OUT | SST_DEFAULT_CELL_NBR), ++ SST_SWM_OUT_SPEECH = (SST_PATH_INDEX_SPEECH_OUT | SST_DEFAULT_CELL_NBR), ++ SST_SWM_OUT_RXSPEECH = (SST_PATH_INDEX_RX_SPEECH_OUT | SST_DEFAULT_CELL_NBR), ++ SST_SWM_OUT_VOIP = (SST_PATH_INDEX_VOIP_OUT | SST_DEFAULT_CELL_NBR), ++ SST_SWM_OUT_PCM0 = (SST_PATH_INDEX_PCM0_OUT | SST_DEFAULT_CELL_NBR), ++ SST_SWM_OUT_PCM1 = (SST_PATH_INDEX_PCM1_OUT | SST_DEFAULT_CELL_NBR), ++ SST_SWM_OUT_PCM2 = (SST_PATH_INDEX_PCM2_OUT | SST_DEFAULT_CELL_NBR), ++ SST_SWM_OUT_AWARE = (SST_PATH_INDEX_AWARE_OUT | SST_DEFAULT_CELL_NBR), ++ SST_SWM_OUT_VAD = (SST_PATH_INDEX_VAD_OUT | SST_DEFAULT_CELL_NBR), ++ SST_SWM_OUT_MEDIA0 = (SST_PATH_INDEX_MEDIA0_OUT | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ ++ SST_SWM_OUT_MEDIA1 = (SST_PATH_INDEX_MEDIA1_OUT | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ ++ SST_SWM_OUT_FM = (SST_PATH_INDEX_FM_OUT | SST_DEFAULT_CELL_NBR), ++ SST_SWM_OUT_END = (SST_PATH_INDEX_RESERVED | SST_DEFAULT_CELL_NBR), ++}; ++ ++enum sst_ipc_msg { ++ SST_IPC_IA_CMD = 1, ++ SST_IPC_IA_SET_PARAMS, ++ SST_IPC_IA_GET_PARAMS, ++}; ++ ++enum sst_cmd_type { ++ SST_CMD_BYTES_SET = 1, ++ SST_CMD_BYTES_GET = 2, ++}; ++ ++enum sst_task { ++ SST_TASK_SBA = 1, ++ SST_TASK_FBA_UL, ++ SST_TASK_MMX, ++ SST_TASK_AWARE, ++ SST_TASK_FBA_DL, ++}; ++ ++enum sst_type { ++ SST_TYPE_CMD = 1, ++ SST_TYPE_PARAMS, ++}; ++ ++enum sst_flag { ++ SST_FLAG_BLOCKED = 1, ++ SST_FLAG_NONBLOCK, ++}; ++ ++/* ++ * Enumeration for indexing the gain cells in VB_SET_GAIN DSP command ++ */ ++enum sst_gain_index { ++ /* GAIN IDs for SB task start here */ ++ SST_GAIN_INDEX_MODEM_OUT, ++ SST_GAIN_INDEX_MODEM_IN, ++ SST_GAIN_INDEX_BT_OUT, ++ SST_GAIN_INDEX_BT_IN, ++ SST_GAIN_INDEX_FM_OUT, ++ ++ SST_GAIN_INDEX_FM_IN, ++ SST_GAIN_INDEX_CODEC_OUT0, ++ SST_GAIN_INDEX_CODEC_OUT1, ++ SST_GAIN_INDEX_CODEC_IN0, ++ SST_GAIN_INDEX_CODEC_IN1, ++ ++ SST_GAIN_INDEX_SPROT_LOOP_OUT, ++ SST_GAIN_INDEX_MEDIA_LOOP1_OUT, ++ SST_GAIN_INDEX_MEDIA_LOOP2_OUT, ++ SST_GAIN_INDEX_RX_SPEECH_OUT, ++ SST_GAIN_INDEX_TX_SPEECH_IN, ++ ++ SST_GAIN_INDEX_SPEECH_OUT, ++ SST_GAIN_INDEX_SPEECH_IN, ++ SST_GAIN_INDEX_HF_OUT, ++ SST_GAIN_INDEX_HF_SNS_OUT, ++ SST_GAIN_INDEX_TONE_IN, ++ ++ SST_GAIN_INDEX_SIDETONE_IN, ++ SST_GAIN_INDEX_PROBE_OUT, ++ SST_GAIN_INDEX_PROBE_IN, ++ SST_GAIN_INDEX_PCM0_IN_LEFT, ++ SST_GAIN_INDEX_PCM0_IN_RIGHT, ++ ++ SST_GAIN_INDEX_PCM1_OUT_LEFT, ++ SST_GAIN_INDEX_PCM1_OUT_RIGHT, ++ SST_GAIN_INDEX_PCM1_IN_LEFT, ++ SST_GAIN_INDEX_PCM1_IN_RIGHT, ++ SST_GAIN_INDEX_PCM2_OUT_LEFT, ++ ++ SST_GAIN_INDEX_PCM2_OUT_RIGHT, ++ SST_GAIN_INDEX_VOIP_OUT, ++ SST_GAIN_INDEX_VOIP_IN, ++ SST_GAIN_INDEX_AWARE_OUT, ++ SST_GAIN_INDEX_VAD_OUT, ++ ++ /* Gain IDs for FBA task start here */ ++ SST_GAIN_INDEX_VOICE_UL, ++ ++ /* Gain IDs for MMX task start here */ ++ SST_GAIN_INDEX_MEDIA0_IN_LEFT, ++ SST_GAIN_INDEX_MEDIA0_IN_RIGHT, ++ SST_GAIN_INDEX_MEDIA1_IN_LEFT, ++ SST_GAIN_INDEX_MEDIA1_IN_RIGHT, ++ ++ SST_GAIN_INDEX_MEDIA2_IN_LEFT, ++ SST_GAIN_INDEX_MEDIA2_IN_RIGHT, ++ ++ SST_GAIN_INDEX_GAIN_END ++}; ++ ++/* ++ * Audio DSP module IDs specified by FW spec ++ * TODO: Update with all modules ++ */ ++enum sst_module_id { ++ SST_MODULE_ID_GAIN_CELL = 0x0067, ++ SST_MODULE_ID_SPROT = 0x006D, ++ SST_MODULE_ID_NR = 0x0076, ++ SST_MODULE_ID_BWX = 0x0077, ++ SST_MODULE_ID_DRP = 0x0078, ++ SST_MODULE_ID_MDRP = 0x0079, ++ ++ SST_MODULE_ID_ANA = 0x007A, ++ SST_MODULE_ID_AEC = 0x007B, ++ SST_MODULE_ID_NR_SNS = 0x007C, ++ SST_MODULE_ID_SER = 0x007D, ++ SST_MODULE_ID_AGC = 0x007E, ++ ++ SST_MODULE_ID_CNI = 0x007F, ++ SST_MODULE_ID_CONTEXT_ALGO_AWARE = 0x0080, ++ SST_MODULE_ID_FIR_24 = 0x0081, ++ SST_MODULE_ID_IIR_24 = 0x0082, ++ SST_MODULE_ID_FILT_DCR = 0x0082, ++ ++ SST_MODULE_ID_ASRC = 0x0083, ++ SST_MODULE_ID_TONE_GEN = 0x0084, ++ SST_MODULE_ID_BMF = 0x0086, ++ SST_MODULE_ID_EDL = 0x0087, ++ SST_MODULE_ID_GLC = 0x0088, ++ ++ SST_MODULE_ID_FIR_16 = 0x0089, ++ SST_MODULE_ID_IIR_16 = 0x008A, ++ SST_MODULE_ID_DNR = 0x008B, ++ ++ SST_MODULE_ID_CNI_TX = 0x0090, ++ SST_MODULE_ID_REF_LINE = 0x0091, ++ SST_MODULE_ID_VOLUME = 0x0092, ++ ++ SST_MODULE_ID_TASK = 0xFFFF ++}; ++ ++enum sst_cmd { ++ SBA_IDLE = 14, ++ SBA_VB_SET_SPEECH_PATH = 26, ++ MMX_SET_GAIN = 33, ++ SBA_VB_SET_GAIN = 33, ++ FBA_VB_RX_CNI = 35, ++ MMX_SET_GAIN_TIMECONST = 36, ++ SBA_VB_SET_TIMECONST = 36, ++ FBA_VB_ANA = 37, ++ FBA_VB_SET_FIR = 38, ++ FBA_VB_SET_IIR = 39, ++ FBA_VB_AEC = 47, ++ FBA_VB_NR_UL = 48, ++ FBA_VB_AGC = 49, ++ FBA_VB_NR_DL = 55, ++ SBA_PROBE = 66, ++ MMX_PROBE = 66, ++ FBA_VB_SET_BIQUAD_D_C = 69, ++ FBA_VB_DUAL_BAND_COMP = 70, ++ FBA_VB_SNS = 72, ++ FBA_VB_SER = 78, ++ FBA_VB_TX_CNI = 80, ++ SBA_VB_START = 85, ++ FBA_VB_SET_REF_LINE = 94, ++ FBA_VB_SET_DELAY_LINE = 95, ++ FBA_VB_BWX = 104, ++ FBA_VB_GMM = 105, ++ FBA_VB_GLC = 107, ++ FBA_VB_BMF = 111, ++ FBA_VB_DNR = 113, ++ MMX_SET_SWM = 114, ++ SBA_SET_SWM = 114, ++ SBA_SET_MDRP = 116, ++ SBA_HW_SET_SSP = 117, ++ SBA_SET_MEDIA_LOOP_MAP = 118, ++ SBA_SET_MEDIA_PATH = 119, ++ MMX_SET_MEDIA_PATH = 119, ++ SBA_VB_LPRO = 126, ++ SBA_VB_SET_FIR = 128, ++ SBA_VB_SET_IIR = 129, ++ SBA_SET_SSP_SLOT_MAP = 130, ++ AWARE_ENV_CLASS_PARAMS = 130, ++}; ++ ++enum sst_dsp_switch { ++ SST_SWITCH_OFF = 0, ++ SST_SWITCH_ON = 3, ++}; ++ ++enum sst_path_switch { ++ SST_PATH_OFF = 0, ++ SST_PATH_ON = 1, ++}; ++ ++enum sst_swm_state { ++ SST_SWM_OFF = 0, ++ SST_SWM_ON = 3, ++}; ++ ++#define SST_FILL_LOCATION_IDS(dst, cell_idx, pipe_id) do { \ ++ dst.location_id.p.cell_nbr_idx = (cell_idx); \ ++ dst.location_id.p.path_id = (pipe_id); \ ++ } while (0) ++#define SST_FILL_LOCATION_ID(dst, loc_id) (\ ++ dst.location_id.f = (loc_id)) ++#define SST_FILL_MODULE_ID(dst, mod_id) (\ ++ dst.module_id = (mod_id)) ++ ++#define SST_FILL_DESTINATION1(dst, id) do { \ ++ SST_FILL_LOCATION_ID(dst, (id) & 0xFFFF); \ ++ SST_FILL_MODULE_ID(dst, ((id) & 0xFFFF0000) >> 16); \ ++ } while (0) ++#define SST_FILL_DESTINATION2(dst, loc_id, mod_id) do { \ ++ SST_FILL_LOCATION_ID(dst, loc_id); \ ++ SST_FILL_MODULE_ID(dst, mod_id); \ ++ } while (0) ++#define SST_FILL_DESTINATION3(dst, cell_idx, path_id, mod_id) do { \ ++ SST_FILL_LOCATION_IDS(dst, cell_idx, path_id); \ ++ SST_FILL_MODULE_ID(dst, mod_id); \ ++ } while (0) ++ ++#define SST_FILL_DESTINATION(level, dst, ...) \ ++ SST_FILL_DESTINATION##level(dst, __VA_ARGS__) ++#define SST_FILL_DEFAULT_DESTINATION(dst) \ ++ SST_FILL_DESTINATION(2, dst, SST_DEFAULT_LOCATION_ID, SST_DEFAULT_MODULE_ID) ++ ++struct sst_destination_id { ++ union sst_location_id { ++ struct { ++ u8 cell_nbr_idx; /* module index */ ++ u8 path_id; /* pipe_id */ ++ } __packed p; /* part */ ++ u16 f; /* full */ ++ } __packed location_id; ++ u16 module_id; ++} __packed; ++ ++struct sst_dsp_header { ++ struct sst_destination_id dst; ++ u16 command_id; ++ u16 length; ++} __packed; ++ ++/* ++ * ++ * Common Commands ++ * ++ */ ++struct sst_cmd_generic { ++ struct sst_dsp_header header; ++} __packed; ++ ++struct swm_input_ids { ++ struct sst_destination_id input_id; ++} __packed; ++ ++struct sst_cmd_set_swm { ++ struct sst_dsp_header header; ++ struct sst_destination_id output_id; ++ u16 switch_state; ++ u16 nb_inputs; ++ struct swm_input_ids input[SST_CMD_SWM_MAX_INPUTS]; ++} __packed; ++ ++struct sst_cmd_set_media_path { ++ struct sst_dsp_header header; ++ u16 switch_state; ++} __packed; ++ ++struct sst_cmd_set_speech_path { ++ struct sst_dsp_header header; ++ u16 switch_state; ++ struct { ++ u16 rsvd:8; ++ u16 sample_length:2; ++ u16 rate:3; ++ u16 format:3; ++ } config; ++} __packed; ++ ++struct gain_cell { ++ struct sst_destination_id dest; ++ s16 cell_gain_left; ++ s16 cell_gain_right; ++ u16 gain_time_constant; ++} __packed; ++ ++#define NUM_GAIN_CELLS 1 ++struct sst_cmd_set_gain_dual { ++ struct sst_dsp_header header; ++ u16 gain_cell_num; ++ struct gain_cell cell_gains[NUM_GAIN_CELLS]; ++} __packed; ++ ++struct sst_cmd_set_params { ++ struct sst_destination_id dst; ++ u16 command_id; ++ char params[0]; ++} __packed; ++ ++/* ++ * ++ * Media (MMX) commands ++ * ++ */ ++ ++/* ++ * ++ * SBA commands ++ * ++ */ ++struct sst_cmd_sba_vb_start { ++ struct sst_dsp_header header; ++} __packed; ++ ++union sba_media_loop_params { ++ struct { ++ u16 rsvd:8; ++ u16 sample_length:2; ++ u16 rate:3; ++ u16 format:3; ++ } part; ++ u16 full; ++} __packed; ++ ++struct sst_cmd_sba_set_media_loop_map { ++ struct sst_dsp_header header; ++ u16 switch_state; ++ union sba_media_loop_params param; ++ u16 map; ++} __packed; ++ ++enum sst_ssp_mode { ++ SSP_MODE_MASTER = 0, ++ SSP_MODE_SLAVE = 1, ++}; ++ ++enum sst_ssp_pcm_mode { ++ SSP_PCM_MODE_NORMAL = 0, ++ SSP_PCM_MODE_NETWORK = 1, ++}; ++ ++enum sst_ssp_duplex { ++ SSP_DUPLEX = 0, ++ SSP_RX = 1, ++ SSP_TX = 2, ++}; ++ ++enum sst_ssp_fs_frequency { ++ SSP_FS_8_KHZ = 0, ++ SSP_FS_16_KHZ = 1, ++ SSP_FS_44_1_KHZ = 2, ++ SSP_FS_48_KHZ = 3, ++}; ++ ++enum sst_ssp_fs_polarity { ++ SSP_FS_ACTIVE_LOW = 0, ++ SSP_FS_ACTIVE_HIGH = 1, ++}; ++ ++enum sst_ssp_protocol { ++ SSP_MODE_PCM = 0, ++ SSP_MODE_I2S = 1, ++}; ++ ++enum sst_ssp_port_id { ++ SSP_MODEM = 0, ++ SSP_BT = 1, ++ SSP_FM = 2, ++ SSP_CODEC = 3, ++}; ++ ++struct sst_cmd_sba_hw_set_ssp { ++ struct sst_dsp_header header; ++ u16 selection; /* 0:SSP0(def), 1:SSP1, 2:SSP2 */ ++ ++ u16 switch_state; ++ ++ u16 nb_bits_per_slots:6; /* 0-32 bits, 24 (def) */ ++ u16 nb_slots:4; /* 0-8: slots per frame */ ++ u16 mode:3; /* 0:Master, 1: Slave */ ++ u16 duplex:3; ++ ++ u16 active_tx_slot_map:8; /* Bit map, 0:off, 1:on */ ++ u16 reserved1:8; ++ ++ u16 active_rx_slot_map:8; /* Bit map 0: Off, 1:On */ ++ u16 reserved2:8; ++ ++ u16 frame_sync_frequency; ++ ++ u16 frame_sync_polarity:8; ++ u16 data_polarity:8; ++ ++ u16 frame_sync_width; /* 1 to N clocks */ ++ u16 ssp_protocol:8; ++ u16 start_delay:8; /* Start delay in terms of clock ticks */ ++} __packed; ++ ++#define SST_MAX_TDM_SLOTS 8 ++ ++struct sst_param_sba_ssp_slot_map { ++ struct sst_dsp_header header; ++ ++ u16 param_id; ++ u16 param_len; ++ u16 ssp_index; ++ ++ u8 rx_slot_map[SST_MAX_TDM_SLOTS]; ++ u8 tx_slot_map[SST_MAX_TDM_SLOTS]; ++} __packed; ++ ++enum { ++ SST_PROBE_EXTRACTOR = 0, ++ SST_PROBE_INJECTOR = 1, ++}; ++ ++struct sst_cmd_probe { ++ struct sst_dsp_header header; ++ ++ u16 switch_state; ++ struct sst_destination_id probe_dst; ++ ++ u16 shared_mem:1; ++ u16 probe_in:1; ++ u16 probe_out:1; ++ u16 rsvd_1:13; ++ ++ u16 rsvd_2:5; ++ u16 probe_mode:2; ++ u16 rsvd_3:1; ++ u16 sample_length:2; ++ u16 rate:3; ++ u16 format:3; ++ ++ u16 sm_buf_id; ++ ++ u16 gain[6]; ++ u16 rsvd_4[9]; ++} __packed; ++ ++struct sst_probe_config { ++ const char *name; ++ u16 loc_id; ++ u16 mod_id; ++ u8 task_id; ++ struct pcm_cfg { ++ u8 s_length:2; ++ u8 rate:3; ++ u8 format:3; ++ } cfg; ++}; ++ ++int sst_mix_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); ++int sst_mix_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); ++#endif +diff --git a/sound/soc/intel/platform-libs/controls_v2_dpcm.c b/sound/soc/intel/platform-libs/controls_v2_dpcm.c +new file mode 100644 +index 0000000..311c043 +--- /dev/null ++++ b/sound/soc/intel/platform-libs/controls_v2_dpcm.c +@@ -0,0 +1,1803 @@ ++/* ++ * controls_v2_dpcm.c - Intel MID Platform driver DPCM ALSA controls for Mrfld ++ * ++ * Copyright (C) 2013 Intel Corp ++ * Author: Omair Mohammed Abdullah ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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 ++ ++#include ++#include ++#include ++#include "../platform_ipc_v2.h" ++#include "../sst_platform.h" ++#include "../sst_platform_pvt.h" ++#include "controls_v2.h" ++#include "sst_widgets.h" ++ ++static inline void sst_fill_byte_control(char *param, ++ u8 ipc_msg, u8 block, ++ u8 task_id, u8 pipe_id, ++ u16 len, void *cmd_data) ++{ ++ ++ struct snd_sst_bytes_v2 *byte_data = (struct snd_sst_bytes_v2 *)param; ++ byte_data->type = SST_CMD_BYTES_SET; ++ byte_data->ipc_msg = ipc_msg; ++ byte_data->block = block; ++ byte_data->task_id = task_id; ++ byte_data->pipe_id = pipe_id; ++ ++ if (len > SST_MAX_BIN_BYTES - sizeof(*byte_data)) { ++ pr_err("%s: command length too big (%u)", __func__, len); ++ len = SST_MAX_BIN_BYTES - sizeof(*byte_data); ++ WARN_ON(1); /* this happens only if code is wrong */ ++ } ++ byte_data->len = len; ++ memcpy(byte_data->bytes, cmd_data, len); ++ print_hex_dump_bytes("writing to lpe: ", DUMP_PREFIX_OFFSET, ++ byte_data, len + sizeof(*byte_data)); ++} ++ ++static int sst_fill_and_send_cmd(struct sst_data *sst, ++ u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id, ++ void *cmd_data, u16 len) ++{ ++ int ret = 0; ++ ++ mutex_lock(&sst->lock); ++ sst_fill_byte_control(sst->byte_stream, ipc_msg, block, task_id, pipe_id, ++ len, cmd_data); ++ ret = sst_dsp->ops->set_generic_params(SST_SET_BYTE_STREAM, ++ sst->byte_stream); ++ mutex_unlock(&sst->lock); ++ ++ return ret; ++} ++ ++static int sst_probe_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct sst_probe_value *v = (void *)kcontrol->private_value; ++ ++ ucontrol->value.enumerated.item[0] = v->val; ++ return 0; ++} ++ ++static int sst_probe_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct sst_probe_value *v = (void *)kcontrol->private_value; ++ const struct soc_enum *e = v->p_enum; ++ ++ if (ucontrol->value.enumerated.item[0] > e->max - 1) ++ return -EINVAL; ++ v->val = ucontrol->value.enumerated.item[0]; ++ return 0; ++} ++ ++int sst_probe_enum_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ struct sst_probe_value *v = (void *)kcontrol->private_value; ++ const struct soc_enum *e = v->p_enum; ++ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; ++ uinfo->count = 1; ++ uinfo->value.enumerated.items = e->max; ++ ++ if (uinfo->value.enumerated.item > e->max - 1) ++ uinfo->value.enumerated.item = e->max - 1; ++ strcpy(uinfo->value.enumerated.name, ++ e->texts[uinfo->value.enumerated.item]); ++ return 0; ++} ++ ++/* ++ * slot map value is a bitfield where each bit represents a FW channel ++ * ++ * 3 2 1 0 # 0 = codec0, 1 = codec1 ++ * RLRLRLRL # 3, 4 = reserved ++ * ++ * e.g. slot 0 rx map = 00001100b -> data from slot 0 goes into codec_in1 L,R ++ */ ++static u8 sst_ssp_slot_map[SST_MAX_TDM_SLOTS] = { ++ 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default rx map */ ++}; ++ ++/* ++ * channel map value is a bitfield where each bit represents a slot ++ * ++ * 76543210 # 0 = slot 0, 1 = slot 1 ++ * ++ * e.g. codec1_0 tx map = 00000101b -> data from codec_out1_0 goes into slot 0, 2 ++ */ ++static u8 sst_ssp_channel_map[SST_MAX_TDM_SLOTS] = { ++ 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default tx map */ ++}; ++ ++static int sst_slot_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct soc_enum *e = (void *)kcontrol->private_value; ++ unsigned int ctl_no = e->reg; ++ unsigned int is_tx = e->reg2; ++ unsigned int val, mux; ++ u8 *map = is_tx ? sst_ssp_channel_map : sst_ssp_slot_map; ++ ++ val = 1 << ctl_no; ++ /* search which slot/channel has this bit set - there should be only one */ ++ for (mux = e->max; mux > 0; mux--) ++ if (map[mux - 1] & val) ++ break; ++ ++ ucontrol->value.enumerated.item[0] = mux; ++ pr_debug("%s: %s - %s map = %#x\n", __func__, is_tx ? "tx channel" : "rx slot", ++ e->texts[mux], mux ? map[mux - 1] : -1); ++ return 0; ++} ++ ++/* ++ * (de)interleaver controls are defined in opposite sense to be user-friendly ++ * ++ * Instead of the enum value being the value set to the register, it is the ++ * register address; and the kcontrol_no is the value written to the register. ++ * ++ * This means that whenever an enum is set, we need to clear the bit ++ * for that kcontrol_no for all the interleaver OR deinterleaver registers ++ */ ++static int sst_slot_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct soc_enum *e = (void *)kcontrol->private_value; ++ int i; ++ unsigned int ctl_no = e->reg; ++ unsigned int is_tx = e->reg2; ++ unsigned int slot_channel_no; ++ unsigned int val, mux; ++ u8 *map = is_tx ? sst_ssp_channel_map : sst_ssp_slot_map; ++ ++ val = 1 << ctl_no; ++ mux = ucontrol->value.enumerated.item[0]; ++ if (mux > e->max - 1) ++ return -EINVAL; ++ ++ /* first clear all registers of this bit */ ++ for (i = 0; i < e->max; i++) ++ map[i] &= ~val; ++ ++ if (mux == 0) /* kctl set to 'none' */ ++ return 0; ++ ++ /* offset by one to take "None" into account */ ++ slot_channel_no = mux - 1; ++ map[slot_channel_no] |= val; ++ ++ pr_debug("%s: %s %s map = %#x\n", __func__, is_tx ? "tx channel" : "rx slot", ++ e->texts[mux], map[slot_channel_no]); ++ return 0; ++} ++ ++/* assumes a boolean mux */ ++static inline bool get_mux_state(struct sst_data *sst, unsigned int reg, unsigned int shift) ++{ ++ return (sst_reg_read(sst, reg, shift, 1) == 1); ++} ++ ++static int sst_mux_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); ++ struct snd_soc_dapm_widget *widget = wlist->widgets[0]; ++ struct sst_data *sst = snd_soc_platform_get_drvdata(widget->platform); ++ struct soc_enum *e = (void *)kcontrol->private_value; ++ unsigned int max = e->max - 1; ++ ++ ucontrol->value.enumerated.item[0] = sst_reg_read(sst, e->reg, e->shift_l, max); ++ return 0; ++} ++ ++static int sst_mux_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); ++ struct snd_soc_dapm_widget *widget = wlist->widgets[0]; ++ struct sst_data *sst = snd_soc_platform_get_drvdata(widget->platform); ++ struct soc_enum *e = (void *)kcontrol->private_value; ++ struct snd_soc_dapm_update update; ++ unsigned int max = e->max - 1; ++ unsigned int mask = (1 << fls(max)) - 1; ++ unsigned int mux, val; ++ ++ if (ucontrol->value.enumerated.item[0] > e->max - 1) ++ return -EINVAL; ++ ++ mux = ucontrol->value.enumerated.item[0]; ++ val = sst_reg_write(sst, e->reg, e->shift_l, max, mux); ++ ++ pr_debug("%s: reg[%d] = %#x\n", __func__, e->reg, val); ++ ++ widget->value = val; ++ update.kcontrol = kcontrol; ++ update.widget = widget; ++ update.reg = e->reg; ++ update.mask = mask; ++ update.val = val; ++ ++ widget->dapm->update = &update; ++ snd_soc_dapm_mux_update_power(widget, kcontrol, mux, e); ++ widget->dapm->update = NULL; ++ return 0; ++} ++ ++static int sst_mode_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); ++ struct sst_data *sst = snd_soc_platform_get_drvdata(platform); ++ struct soc_enum *e = (void *)kcontrol->private_value; ++ unsigned int max = e->max - 1; ++ ++ ucontrol->value.enumerated.item[0] = sst_reg_read(sst, e->reg, e->shift_l, max); ++ return 0; ++} ++ ++static int sst_mode_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); ++ struct sst_data *sst = snd_soc_platform_get_drvdata(platform); ++ struct soc_enum *e = (void *)kcontrol->private_value; ++ unsigned int max = e->max - 1; ++ unsigned int val; ++ ++ if (ucontrol->value.enumerated.item[0] > e->max - 1) ++ return -EINVAL; ++ ++ val = sst_reg_write(sst, e->reg, e->shift_l, max, ucontrol->value.enumerated.item[0]); ++ pr_debug("%s: reg[%d] - %#x\n", __func__, e->reg, val); ++ return 0; ++} ++ ++static void sst_send_algo_cmd(struct sst_data *sst, ++ struct sst_algo_control *bc) ++{ ++ int len; ++ struct sst_cmd_set_params *cmd; ++ ++ if (bc->params == NULL) ++ return; ++ ++ len = sizeof(cmd->dst) + sizeof(cmd->command_id) + bc->max; ++ ++ cmd = kzalloc(len + bc->max, GFP_KERNEL); ++ if (cmd == NULL) { ++ pr_err("Failed to send cmd, kzalloc failed\n"); ++ return; ++ } ++ ++ SST_FILL_DESTINATION(2, cmd->dst, bc->pipe_id, bc->module_id); ++ cmd->command_id = bc->cmd_id; ++ memcpy(cmd->params, bc->params, bc->max); ++ ++ sst_fill_and_send_cmd(sst, SST_IPC_IA_SET_PARAMS, SST_FLAG_BLOCKED, ++ bc->task_id, 0, cmd, len); ++ kfree(cmd); ++ ++} ++ ++static void sst_find_and_send_pipe_algo(struct snd_soc_platform *platform, ++ struct snd_soc_dapm_widget *w) ++{ ++ struct sst_algo_control *bc; ++ struct sst_data *sst = snd_soc_platform_get_drvdata(platform); ++ struct sst_ids *ids = w->priv; ++ struct module *algo = NULL; ++ ++ pr_debug("Enter:%s, widget=%s\n", __func__, w->name); ++ ++ list_for_each_entry(algo, &ids->algo_list, node) { ++ bc = (void *)algo->kctl->private_value; ++ ++ pr_debug("Found algo control name =%s pipe=%s\n", algo->kctl->id.name, w->name); ++ sst_send_algo_cmd(sst, bc); ++ } ++} ++ ++int sst_algo_bytes_ctl_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ struct sst_algo_control *bc = (void *)kcontrol->private_value; ++ struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); ++ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; ++ uinfo->count = bc->max; ++ ++ if (bc->params == NULL) { ++ bc->params = devm_kzalloc(platform->dev, bc->max, GFP_KERNEL); ++ if (bc->params == NULL) { ++ pr_err("kzalloc failed\n"); ++ return -ENOMEM; ++ } ++ } ++ return 0; ++} ++ ++static int sst_algo_control_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct sst_algo_control *bc = (void *)kcontrol->private_value; ++ ++ pr_debug("in %s\n", __func__); ++ switch (bc->type) { ++ case SST_ALGO_PARAMS: ++ if (bc->params) ++ memcpy(ucontrol->value.bytes.data, bc->params, bc->max); ++ break; ++ case SST_ALGO_BYPASS: ++ ucontrol->value.integer.value[0] = bc->bypass ? 1 : 0; ++ pr_debug("%s: bypass %d\n", __func__, bc->bypass); ++ break; ++ default: ++ pr_err("Invalid Input- algo type:%d\n", bc->type); ++ return -EINVAL; ++ ++ } ++ return 0; ++} ++ ++static int sst_algo_control_set(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); ++ struct sst_data *sst = snd_soc_platform_get_drvdata(platform); ++ struct sst_algo_control *bc = (void *)kcontrol->private_value; ++ ++ pr_debug("in %s control_name=%s\n", __func__, kcontrol->id.name); ++ switch (bc->type) { ++ case SST_ALGO_PARAMS: ++ if (bc->params) ++ memcpy(bc->params, ucontrol->value.bytes.data, bc->max); ++ break; ++ case SST_ALGO_BYPASS: ++ bc->bypass = !!ucontrol->value.integer.value[0]; ++ pr_debug("%s: Mute %d\n", __func__, bc->bypass); ++ break; ++ default: ++ pr_err("Invalid Input- algo type:%ld\n", ucontrol->value.integer.value[0]); ++ return -EINVAL; ++ } ++ /*if pipe is enabled, need to send the algo params from here */ ++ if (bc->w && bc->w->power) ++ sst_send_algo_cmd(sst, bc); ++ ++ return 0; ++} ++ ++static int sst_gain_ctl_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value; ++ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = mc->stereo ? 2 : 1; ++ uinfo->value.integer.min = mc->min; ++ uinfo->value.integer.max = mc->max; ++ return 0; ++} ++ ++static void sst_send_gain_cmd(struct sst_data *sst, struct sst_gain_value *gv, ++ u16 task_id, u16 loc_id, u16 module_id, int mute) ++{ ++ struct sst_cmd_set_gain_dual cmd; ++ pr_debug("%s", __func__); ++ ++ cmd.header.command_id = MMX_SET_GAIN; ++ SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); ++ cmd.gain_cell_num = 1; ++ ++ if (mute || gv->mute) { ++ cmd.cell_gains[0].cell_gain_left = SST_GAIN_MIN_VALUE; ++ cmd.cell_gains[0].cell_gain_right = SST_GAIN_MIN_VALUE; ++ } else { ++ cmd.cell_gains[0].cell_gain_left = gv->l_gain; ++ cmd.cell_gains[0].cell_gain_right = gv->r_gain; ++ } ++ SST_FILL_DESTINATION(2, cmd.cell_gains[0].dest, ++ loc_id, module_id); ++ cmd.cell_gains[0].gain_time_constant = gv->ramp_duration; ++ ++ cmd.header.length = sizeof(struct sst_cmd_set_gain_dual) ++ - sizeof(struct sst_dsp_header); ++ ++ sst_fill_and_send_cmd(sst, SST_IPC_IA_SET_PARAMS, SST_FLAG_BLOCKED, ++ task_id, 0, &cmd, ++ sizeof(cmd.header) + cmd.header.length); ++} ++ ++static int sst_gain_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value; ++ struct sst_gain_value *gv = mc->gain_val; ++ ++ switch (mc->type) { ++ case SST_GAIN_TLV: ++ ucontrol->value.integer.value[0] = gv->l_gain; ++ ucontrol->value.integer.value[1] = gv->r_gain; ++ pr_debug("%s: Volume %d, %d\n", __func__, gv->l_gain, gv->r_gain); ++ break; ++ case SST_GAIN_MUTE: ++ ucontrol->value.integer.value[0] = gv->mute ? 1 : 0; ++ pr_debug("%s: Mute %d\n", __func__, gv->mute); ++ break; ++ case SST_GAIN_RAMP_DURATION: ++ ucontrol->value.integer.value[0] = gv->ramp_duration; ++ pr_debug("%s: RampDuration %d\n", __func__, gv->ramp_duration); ++ break; ++ default: ++ pr_err("Invalid Input- gain type:%d\n", mc->type); ++ return -EINVAL; ++ }; ++ return 0; ++} ++ ++static int sst_gain_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); ++ struct sst_data *sst = snd_soc_platform_get_drvdata(platform); ++ struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value; ++ struct sst_gain_value *gv = mc->gain_val; ++ ++ switch (mc->type) { ++ case SST_GAIN_TLV: ++ gv->l_gain = ucontrol->value.integer.value[0]; ++ gv->r_gain = ucontrol->value.integer.value[1]; ++ pr_debug("%s: Volume %d, %d\n", __func__, gv->l_gain, gv->r_gain); ++ break; ++ case SST_GAIN_MUTE: ++ gv->mute = !!ucontrol->value.integer.value[0]; ++ pr_debug("%s: Mute %d\n", __func__, gv->mute); ++ break; ++ case SST_GAIN_RAMP_DURATION: ++ gv->ramp_duration = ucontrol->value.integer.value[0]; ++ pr_debug("%s: RampDuration %d\n", __func__, gv->ramp_duration); ++ break; ++ default: ++ pr_err("Invalid Input- gain type:%d\n", mc->type); ++ return -EINVAL; ++ }; ++ ++ if (mc->w && mc->w->power) ++ sst_send_gain_cmd(sst, gv, mc->task_id, ++ mc->pipe_id | mc->instance_id, mc->module_id, 0); ++ return 0; ++} ++ ++static const DECLARE_TLV_DB_SCALE(sst_gain_tlv_common, SST_GAIN_MIN_VALUE * 10, 10, 0); ++ ++/* Look up table to convert MIXER SW bit regs to SWM inputs */ ++static const uint swm_mixer_input_ids[SST_SWM_INPUT_COUNT] = { ++ [SST_IP_MODEM] = SST_SWM_IN_MODEM, ++ [SST_IP_BT] = SST_SWM_IN_BT, ++ [SST_IP_CODEC0] = SST_SWM_IN_CODEC0, ++ [SST_IP_CODEC1] = SST_SWM_IN_CODEC1, ++ [SST_IP_LOOP0] = SST_SWM_IN_SPROT_LOOP, ++ [SST_IP_LOOP1] = SST_SWM_IN_MEDIA_LOOP1, ++ [SST_IP_LOOP2] = SST_SWM_IN_MEDIA_LOOP2, ++ [SST_IP_SIDETONE] = SST_SWM_IN_SIDETONE, ++ [SST_IP_TXSPEECH] = SST_SWM_IN_TXSPEECH, ++ [SST_IP_SPEECH] = SST_SWM_IN_SPEECH, ++ [SST_IP_TONE] = SST_SWM_IN_TONE, ++ [SST_IP_VOIP] = SST_SWM_IN_VOIP, ++ [SST_IP_PCM0] = SST_SWM_IN_PCM0, ++ [SST_IP_PCM1] = SST_SWM_IN_PCM1, ++ [SST_IP_LOW_PCM0] = SST_SWM_IN_LOW_PCM0, ++ [SST_IP_FM] = SST_SWM_IN_FM, ++ [SST_IP_MEDIA0] = SST_SWM_IN_MEDIA0, ++ [SST_IP_MEDIA1] = SST_SWM_IN_MEDIA1, ++ [SST_IP_MEDIA2] = SST_SWM_IN_MEDIA2, ++ [SST_IP_MEDIA3] = SST_SWM_IN_MEDIA3, ++}; ++ ++static int fill_swm_input(struct swm_input_ids *swm_input, unsigned int reg) ++{ ++ uint i, is_set, nb_inputs = 0; ++ u16 input_loc_id; ++ ++ pr_debug("%s:reg value:%#x\n", __func__, reg); ++ for (i = 0; i < SST_SWM_INPUT_COUNT; i++) { ++ is_set = reg & BIT(i); ++ if (!is_set) ++ continue; ++ ++ input_loc_id = swm_mixer_input_ids[i]; ++ SST_FILL_DESTINATION(2, swm_input->input_id, ++ input_loc_id, SST_DEFAULT_MODULE_ID); ++ nb_inputs++; ++ swm_input++; ++ pr_debug("input id:%#x, nb_inputs:%d\n", input_loc_id, nb_inputs); ++ ++ if (nb_inputs == SST_CMD_SWM_MAX_INPUTS) { ++ pr_warn("%s: SET_SWM cmd max inputs reached", __func__); ++ break; ++ } ++ } ++ return nb_inputs; ++} ++ ++static void sst_set_pipe_gain(struct sst_ids *ids, struct sst_data *sst, int mute) ++{ ++ struct sst_gain_mixer_control *mc; ++ struct sst_gain_value *gv; ++ struct module *gain = NULL; ++ ++ list_for_each_entry(gain, &ids->gain_list, node) { ++ struct snd_kcontrol *kctl = gain->kctl; ++ ++ pr_debug("control name=%s", kctl->id.name); ++ mc = (void *)kctl->private_value; ++ gv = mc->gain_val; ++ ++ sst_send_gain_cmd(sst, gv, mc->task_id, ++ mc->pipe_id | mc->instance_id, mc->module_id, mute); ++ } ++} ++ ++static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *k, int event) ++{ ++ struct sst_cmd_set_swm cmd; ++ struct sst_data *sst = snd_soc_platform_get_drvdata(w->platform); ++ struct sst_ids *ids = w->priv; ++ bool set_mixer = false; ++ int val = sst->widget[ids->reg]; ++ ++ pr_debug("%s: widget=%s\n", __func__, w->name); ++ pr_debug("%s: reg[%d] = %#x\n", __func__, ids->reg, val); ++ ++ switch (event) { ++ case SND_SOC_DAPM_PRE_PMU: ++ case SND_SOC_DAPM_POST_PMD: ++ set_mixer = true; ++ break; ++ case SND_SOC_DAPM_POST_REG: ++ if (w->power) ++ set_mixer = true; ++ break; ++ default: ++ set_mixer = false; ++ } ++ ++ if (set_mixer == false) ++ return 0; ++ ++ if (SND_SOC_DAPM_EVENT_ON(event) || ++ event == SND_SOC_DAPM_POST_REG) ++ cmd.switch_state = SST_SWM_ON; ++ else ++ cmd.switch_state = SST_SWM_OFF; ++ ++ SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); ++ /* MMX_SET_SWM == SBA_SET_SWM */ ++ cmd.header.command_id = SBA_SET_SWM; ++ ++ SST_FILL_DESTINATION(2, cmd.output_id, ++ ids->location_id, SST_DEFAULT_MODULE_ID); ++ cmd.nb_inputs = fill_swm_input(&cmd.input[0], val); ++ cmd.header.length = offsetof(struct sst_cmd_set_swm, input) - sizeof(struct sst_dsp_header) ++ + (cmd.nb_inputs * sizeof(cmd.input[0])); ++ ++ sst_fill_and_send_cmd(sst, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, ++ ids->task_id, 0, &cmd, ++ sizeof(cmd.header) + cmd.header.length); ++ return 0; ++} ++ ++/* SBA mixers - 16 inputs */ ++#define SST_SBA_DECLARE_MIX_CONTROLS(kctl_name, mixer_reg) \ ++ static const struct snd_kcontrol_new kctl_name[] = { \ ++ SOC_SINGLE_EXT("modem_in", mixer_reg, SST_IP_MODEM, 1, 0, \ ++ sst_mix_get, sst_mix_put), \ ++ SOC_SINGLE_EXT("bt_in", mixer_reg, SST_IP_BT, 1, 0, \ ++ sst_mix_get, sst_mix_put), \ ++ SOC_SINGLE_EXT("codec_in0", mixer_reg, SST_IP_CODEC0, 1, 0, \ ++ sst_mix_get, sst_mix_put), \ ++ SOC_SINGLE_EXT("codec_in1", mixer_reg, SST_IP_CODEC1, 1, 0, \ ++ sst_mix_get, sst_mix_put), \ ++ SOC_SINGLE_EXT("sprot_loop_in", mixer_reg, SST_IP_LOOP0, 1, 0, \ ++ sst_mix_get, sst_mix_put), \ ++ SOC_SINGLE_EXT("media_loop1_in", mixer_reg, SST_IP_LOOP1, 1, 0, \ ++ sst_mix_get, sst_mix_put), \ ++ SOC_SINGLE_EXT("media_loop2_in", mixer_reg, SST_IP_LOOP2, 1, 0, \ ++ sst_mix_get, sst_mix_put), \ ++ SOC_SINGLE_EXT("sidetone_in", mixer_reg, SST_IP_SIDETONE, 1, 0, \ ++ sst_mix_get, sst_mix_put), \ ++ SOC_SINGLE_EXT("txspeech_in", mixer_reg, SST_IP_TXSPEECH, 1, 0, \ ++ sst_mix_get, sst_mix_put), \ ++ SOC_SINGLE_EXT("speech_in", mixer_reg, SST_IP_SPEECH, 1, 0, \ ++ sst_mix_get, sst_mix_put), \ ++ SOC_SINGLE_EXT("tone_in", mixer_reg, SST_IP_TONE, 1, 0, \ ++ sst_mix_get, sst_mix_put), \ ++ SOC_SINGLE_EXT("voip_in", mixer_reg, SST_IP_VOIP, 1, 0, \ ++ sst_mix_get, sst_mix_put), \ ++ SOC_SINGLE_EXT("pcm0_in", mixer_reg, SST_IP_PCM0, 1, 0, \ ++ sst_mix_get, sst_mix_put), \ ++ SOC_SINGLE_EXT("pcm1_in", mixer_reg, SST_IP_PCM1, 1, 0, \ ++ sst_mix_get, sst_mix_put), \ ++ SOC_SINGLE_EXT("low_pcm0_in", mixer_reg, SST_IP_LOW_PCM0, 1, 0, \ ++ sst_mix_get, sst_mix_put), \ ++ SOC_SINGLE_EXT("fm_in", mixer_reg, SST_IP_FM, 1, 0, \ ++ sst_mix_get, sst_mix_put), \ ++ } ++ ++#define SST_SBA_MIXER_GRAPH_MAP(mix_name) \ ++ { mix_name, "modem_in", "modem_in" }, \ ++ { mix_name, "bt_in", "bt_in" }, \ ++ { mix_name, "codec_in0", "codec_in0" }, \ ++ { mix_name, "codec_in1", "codec_in1" }, \ ++ { mix_name, "sprot_loop_in", "sprot_loop_in" }, \ ++ { mix_name, "media_loop1_in", "media_loop1_in" }, \ ++ { mix_name, "media_loop2_in", "media_loop2_in" }, \ ++ { mix_name, "sidetone_in", "sidetone_in" }, \ ++ { mix_name, "txspeech_in", "txspeech_in" }, \ ++ { mix_name, "speech_in", "speech_in" }, \ ++ { mix_name, "tone_in", "tone_in" }, \ ++ { mix_name, "voip_in", "voip_in" }, \ ++ { mix_name, "pcm0_in", "pcm0_in" }, \ ++ { mix_name, "pcm1_in", "pcm1_in" }, \ ++ { mix_name, "low_pcm0_in", "low_pcm0_in" }, \ ++ { mix_name, "fm_in", "fm_in" } ++ ++#define SST_MMX_DECLARE_MIX_CONTROLS(kctl_name, mixer_reg) \ ++ static const struct snd_kcontrol_new kctl_name[] = { \ ++ SOC_SINGLE_EXT("media0_in", mixer_reg, SST_IP_MEDIA0, 1, 0, \ ++ sst_mix_get, sst_mix_put), \ ++ SOC_SINGLE_EXT("media1_in", mixer_reg, SST_IP_MEDIA1, 1, 0, \ ++ sst_mix_get, sst_mix_put), \ ++ SOC_SINGLE_EXT("media2_in", mixer_reg, SST_IP_MEDIA2, 1, 0, \ ++ sst_mix_get, sst_mix_put), \ ++ SOC_SINGLE_EXT("media3_in", mixer_reg, SST_IP_MEDIA3, 1, 0, \ ++ sst_mix_get, sst_mix_put), \ ++ } ++ ++SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media0_controls, SST_MIX_MEDIA0); ++SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media1_controls, SST_MIX_MEDIA1); ++ ++/* 18 SBA mixers */ ++SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm0_controls, SST_MIX_PCM0); ++SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm1_controls, SST_MIX_PCM1); ++SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm2_controls, SST_MIX_PCM2); ++SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_sprot_l0_controls, SST_MIX_LOOP0); ++SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l1_controls, SST_MIX_LOOP1); ++SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l2_controls, SST_MIX_LOOP2); ++SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_voip_controls, SST_MIX_VOIP); ++SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_aware_controls, SST_MIX_AWARE); ++SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_vad_controls, SST_MIX_VAD); ++SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_hf_sns_controls, SST_MIX_HF_SNS); ++SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_hf_controls, SST_MIX_HF); ++SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_speech_controls, SST_MIX_SPEECH); ++SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_rxspeech_controls, SST_MIX_RXSPEECH); ++SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec0_controls, SST_MIX_CODEC0); ++SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec1_controls, SST_MIX_CODEC1); ++SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_bt_controls, SST_MIX_BT); ++SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_fm_controls, SST_MIX_FM); ++SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_modem_controls, SST_MIX_MODEM); ++ ++static int sst_vb_trigger_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *k, int event) ++{ ++ struct sst_cmd_generic cmd; ++ struct sst_data *sst = snd_soc_platform_get_drvdata(w->platform); ++ ++ pr_debug("Enter:%s, widget=%s\n", __func__, w->name); ++ if (SND_SOC_DAPM_EVENT_ON(event)) ++ cmd.header.command_id = SBA_VB_START; ++ else ++ cmd.header.command_id = SBA_IDLE; ++ ++ SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); ++ cmd.header.length = 0; ++ ++ if (SND_SOC_DAPM_EVENT_ON(event)) ++ sst_dsp->ops->power(true); ++ ++ sst_fill_and_send_cmd(sst, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, ++ SST_TASK_SBA, 0, &cmd, ++ sizeof(cmd.header) + cmd.header.length); ++ ++ if (!SND_SOC_DAPM_EVENT_ON(event)) ++ sst_dsp->ops->power(false); ++ return 0; ++} ++ ++static void sst_send_slot_map(struct sst_data *sst) ++{ ++ struct sst_param_sba_ssp_slot_map cmd; ++ ++ pr_debug("Enter: %s", __func__); ++ ++ SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); ++ cmd.header.command_id = SBA_SET_SSP_SLOT_MAP; ++ cmd.header.length = sizeof(struct sst_param_sba_ssp_slot_map) ++ - sizeof(struct sst_dsp_header); ++ ++ cmd.param_id = SBA_SET_SSP_SLOT_MAP; ++ cmd.param_len = sizeof(cmd.rx_slot_map) + sizeof(cmd.tx_slot_map) + sizeof(cmd.ssp_index); ++ cmd.ssp_index = SSP_CODEC; ++ ++ memcpy(cmd.rx_slot_map, &sst_ssp_slot_map[0], sizeof(cmd.rx_slot_map)); ++ memcpy(cmd.tx_slot_map, &sst_ssp_channel_map[0], sizeof(cmd.tx_slot_map)); ++ ++ sst_fill_and_send_cmd(sst, SST_IPC_IA_SET_PARAMS, SST_FLAG_BLOCKED, ++ SST_TASK_SBA, 0, &cmd, ++ sizeof(cmd.header) + cmd.header.length); ++} ++ ++static int sst_ssp_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *k, int event) ++{ ++ struct sst_cmd_sba_hw_set_ssp cmd; ++ struct sst_data *sst = snd_soc_platform_get_drvdata(w->platform); ++ struct sst_ids *ids = w->priv; ++ static int ssp_active[SST_NUM_SSPS]; ++ unsigned int domain, mux; ++ unsigned int ssp_no = ids->ssp->ssp_number; ++ int domain_shift, mux_shift; ++ const struct sst_ssp_config *config; ++ ++ pr_debug("Enter:%s, widget=%s\n", __func__, w->name); ++ ++ SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); ++ cmd.header.command_id = SBA_HW_SET_SSP; ++ cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp) ++ - sizeof(struct sst_dsp_header); ++ mux_shift = *ids->ssp->mux_shift; ++ mux = (mux_shift == -1) ? 0 : get_mux_state(sst, SST_MUX_REG, mux_shift); ++ domain_shift = (*ids->ssp->domain_shift)[mux]; ++ domain = (domain_shift == -1) ? 0 : get_mux_state(sst, SST_MUX_REG, domain_shift); ++ ++ config = &(*ids->ssp->ssp_config)[mux][domain]; ++ pr_debug("%s: ssp_id: %u, mux: %d, domain: %d\n", __func__, ++ config->ssp_id, mux, domain); ++ ++ if (SND_SOC_DAPM_EVENT_ON(event)) ++ ssp_active[ssp_no]++; ++ else ++ ssp_active[ssp_no]--; ++ ++ pr_debug("%s: ssp_no: %u ssp_active: %d", __func__, ssp_no, ssp_active[ssp_no]); ++ if (ssp_active[ssp_no]) ++ cmd.switch_state = SST_SWITCH_ON; ++ else ++ cmd.switch_state = SST_SWITCH_OFF; ++ ++ cmd.selection = config->ssp_id; ++ cmd.nb_bits_per_slots = config->bits_per_slot; ++ cmd.nb_slots = config->slots; ++ cmd.mode = config->ssp_mode | (config->pcm_mode << 1); ++ cmd.duplex = config->duplex; ++ cmd.active_tx_slot_map = config->active_slot_map; ++ cmd.active_rx_slot_map = config->active_slot_map; ++ cmd.frame_sync_frequency = config->fs_frequency; ++ cmd.frame_sync_polarity = SSP_FS_ACTIVE_HIGH; ++ cmd.data_polarity = 1; ++ cmd.frame_sync_width = config->fs_width; ++ cmd.ssp_protocol = config->ssp_protocol; ++ cmd.start_delay = config->start_delay; ++ cmd.reserved1 = cmd.reserved2 = 0xFF; ++ ++ sst_fill_and_send_cmd(sst, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, ++ SST_TASK_SBA, 0, &cmd, ++ sizeof(cmd.header) + cmd.header.length); ++ ++ if (SND_SOC_DAPM_EVENT_ON(event)) { ++ sst_find_and_send_pipe_algo(w->platform, w); ++ sst_send_slot_map(sst); ++ sst_set_pipe_gain(ids, sst, 0); ++ } ++ return 0; ++} ++ ++static int sst_set_speech_path(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *k, int event) ++{ ++ struct sst_cmd_set_speech_path cmd; ++ struct sst_data *sst = snd_soc_platform_get_drvdata(w->platform); ++ struct sst_ids *ids = w->priv; ++ bool is_wideband; ++ static int speech_active; ++ ++ pr_debug("%s: widget=%s\n", __func__, w->name); ++ ++ if (SND_SOC_DAPM_EVENT_ON(event)) { ++ speech_active++; ++ cmd.switch_state = SST_SWITCH_ON; ++ } else { ++ speech_active--; ++ cmd.switch_state = SST_SWITCH_OFF; ++ } ++ ++ SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); ++ ++ cmd.header.command_id = SBA_VB_SET_SPEECH_PATH; ++ cmd.header.length = sizeof(struct sst_cmd_set_speech_path) ++ - sizeof(struct sst_dsp_header); ++ cmd.config.sample_length = 0; ++ cmd.config.rate = 0; /* 8 khz */ ++ cmd.config.format = 0; ++ ++ is_wideband = get_mux_state(sst, SST_MUX_REG, SST_VOICE_MODE_SHIFT); ++ if (is_wideband) ++ cmd.config.rate = 1; /* 16 khz */ ++ ++ if ((SND_SOC_DAPM_EVENT_ON(event) && (speech_active == 1)) || ++ (SND_SOC_DAPM_EVENT_OFF(event) && (speech_active == 0))) ++ sst_fill_and_send_cmd(sst, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, ++ SST_TASK_SBA, 0, &cmd, ++ sizeof(cmd.header) + cmd.header.length); ++ ++ if (SND_SOC_DAPM_EVENT_ON(event)) { ++ sst_find_and_send_pipe_algo(w->platform, w); ++ sst_set_pipe_gain(ids, sst, 0); ++ } ++ ++ return 0; ++ ++} ++ ++static int sst_set_media_path(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *k, int event) ++{ ++ struct sst_cmd_set_media_path cmd; ++ struct sst_data *sst = snd_soc_platform_get_drvdata(w->platform); ++ struct sst_ids *ids = w->priv; ++ ++ pr_debug("%s: widget=%s\n", __func__, w->name); ++ pr_debug("%s: task=%u, location=%#x\n", __func__, ++ ids->task_id, ids->location_id); ++ ++ if (SND_SOC_DAPM_EVENT_ON(event)) ++ cmd.switch_state = SST_PATH_ON; ++ else ++ cmd.switch_state = SST_PATH_OFF; ++ ++ SST_FILL_DESTINATION(2, cmd.header.dst, ++ ids->location_id, SST_DEFAULT_MODULE_ID); ++ ++ /* MMX_SET_MEDIA_PATH == SBA_SET_MEDIA_PATH */ ++ cmd.header.command_id = MMX_SET_MEDIA_PATH; ++ cmd.header.length = sizeof(struct sst_cmd_set_media_path) ++ - sizeof(struct sst_dsp_header); ++ ++ sst_fill_and_send_cmd(sst, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, ++ ids->task_id, 0, &cmd, ++ sizeof(cmd.header) + cmd.header.length); ++ ++ if (SND_SOC_DAPM_EVENT_ON(event)) { ++ sst_find_and_send_pipe_algo(w->platform, w); ++ sst_set_pipe_gain(ids, sst, 0); ++ } ++ ++ return 0; ++} ++ ++static int sst_set_media_loop(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *k, int event) ++{ ++ struct sst_cmd_sba_set_media_loop_map cmd; ++ struct sst_data *sst = snd_soc_platform_get_drvdata(w->platform); ++ struct sst_ids *ids = w->priv; ++ ++ pr_debug("Enter:%s, widget=%s\n", __func__, w->name); ++ if (SND_SOC_DAPM_EVENT_ON(event)) ++ cmd.switch_state = SST_SWITCH_ON; ++ else ++ cmd.switch_state = SST_SWITCH_OFF; ++ ++ SST_FILL_DESTINATION(2, cmd.header.dst, ++ ids->location_id, SST_DEFAULT_MODULE_ID); ++ ++ cmd.header.command_id = SBA_SET_MEDIA_LOOP_MAP; ++ cmd.header.length = sizeof(struct sst_cmd_sba_set_media_loop_map) ++ - sizeof(struct sst_dsp_header); ++ cmd.param.part.rate = 2; /* 48khz */ ++ ++ cmd.param.part.format = ids->format; /* stereo/Mono */ ++ cmd.param.part.sample_length = 1; /* 24bit left justified*/ ++ cmd.map = 0; /* Algo sequence: Gain - DRP - FIR - IIR */ ++ ++ sst_fill_and_send_cmd(sst, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, ++ SST_TASK_SBA, 0, &cmd, ++ sizeof(cmd.header) + cmd.header.length); ++ if (SND_SOC_DAPM_EVENT_ON(event)) { ++ sst_find_and_send_pipe_algo(w->platform, w); ++ sst_set_pipe_gain(ids, sst, 0); ++ } ++ return 0; ++} ++ ++static int sst_send_probe_cmd(struct sst_data *sst, u16 probe_pipe_id, ++ int mode, int switch_state, ++ const struct sst_probe_config *probe_cfg) ++{ ++ struct sst_cmd_probe cmd; ++ ++ memset(&cmd, 0, sizeof(cmd)); ++ ++ SST_FILL_DESTINATION(3, cmd.header.dst, SST_DEFAULT_CELL_NBR, ++ probe_pipe_id, SST_DEFAULT_MODULE_ID); ++ cmd.header.command_id = SBA_PROBE; ++ cmd.header.length = sizeof(struct sst_cmd_probe) ++ - sizeof(struct sst_dsp_header); ++ cmd.switch_state = switch_state; ++ ++ SST_FILL_DESTINATION(2, cmd.probe_dst, ++ probe_cfg->loc_id, probe_cfg->mod_id); ++ ++ cmd.shared_mem = 1; ++ cmd.probe_in = 0; ++ cmd.probe_out = 0; ++ ++ cmd.probe_mode = mode; ++ cmd.sample_length = probe_cfg->cfg.s_length; ++ cmd.rate = probe_cfg->cfg.rate; ++ cmd.format = probe_cfg->cfg.format; ++ cmd.sm_buf_id = 1; ++ ++ return sst_fill_and_send_cmd(sst, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, ++ probe_cfg->task_id, 0, &cmd, ++ sizeof(cmd.header) + cmd.header.length); ++} ++ ++static const struct snd_kcontrol_new sst_probe_controls[]; ++static const struct sst_probe_config sst_probes[]; ++ ++#define SST_MAX_PROBE_STREAMS 8 ++int sst_dpcm_probe_send(struct snd_soc_platform *platform, u16 probe_pipe_id, ++ int substream, int direction, bool on) ++{ ++ int switch_state = on ? SST_SWITCH_ON : SST_SWITCH_OFF; ++ struct sst_data *sst = snd_soc_platform_get_drvdata(platform); ++ const struct sst_probe_config *probe_cfg; ++ struct sst_probe_value *probe_val; ++ char *type; ++ int offset; ++ int mode; ++ ++ if (direction == SNDRV_PCM_STREAM_CAPTURE) { ++ mode = SST_PROBE_EXTRACTOR; ++ offset = 0; ++ type = "extractor"; ++ } else { ++ mode = SST_PROBE_INJECTOR; ++ offset = SST_MAX_PROBE_STREAMS; ++ type = "injector"; ++ } ++ /* get the value of the probe connection kcontrol */ ++ probe_val = (void *)sst_probe_controls[substream + offset].private_value; ++ probe_cfg = &sst_probes[probe_val->val]; ++ ++ pr_debug("%s: substream=%d, direction=%d\n", __func__, substream, direction); ++ pr_debug("%s: %s probe point at %s\n", __func__, type, probe_cfg->name); ++ ++ return sst_send_probe_cmd(sst, probe_pipe_id, mode, switch_state, probe_cfg); ++} ++ ++static const struct snd_kcontrol_new sst_mix_sw_aware = ++ SOC_SINGLE_EXT("switch", SST_MIX_SWITCH, 0, 1, 0, ++ sst_mix_get, sst_mix_put); ++ ++static const char * const sst_bt_fm_texts[] = { ++ "fm", "bt", ++}; ++ ++static const struct snd_kcontrol_new sst_bt_fm_mux = ++ SST_SSP_MUX_CTL("ssp1_out", 0, SST_MUX_REG, SST_BT_FM_MUX_SHIFT, sst_bt_fm_texts, ++ sst_mux_get, sst_mux_put); ++ ++#define SST_SSP_CODEC_MUX 0 ++#define SST_SSP_CODEC_DOMAIN 0 ++#define SST_SSP_MODEM_MUX 0 ++#define SST_SSP_MODEM_DOMAIN 0 ++#define SST_SSP_FM_MUX 0 ++#define SST_SSP_FM_DOMAIN 0 ++#define SST_SSP_BT_MUX 1 ++#define SST_SSP_BT_NB_DOMAIN 0 ++#define SST_SSP_BT_WB_DOMAIN 1 ++ ++static const int sst_ssp_mux_shift[SST_NUM_SSPS] = { ++ [SST_SSP0] = -1, /* no register shift, i.e. single mux value */ ++ [SST_SSP1] = SST_BT_FM_MUX_SHIFT, ++ [SST_SSP2] = -1, ++}; ++ ++static const int sst_ssp_domain_shift[SST_NUM_SSPS][SST_MAX_SSP_MUX] = { ++ [SST_SSP0][0] = -1, /* no domain shift, i.e. single domain */ ++ [SST_SSP1] = { ++ [SST_SSP_FM_MUX] = -1, ++ [SST_SSP_BT_MUX] = SST_BT_MODE_SHIFT, ++ }, ++ [SST_SSP2][0] = -1, ++}; ++ ++static const struct sst_ssp_config ++sst_ssp_configs[SST_NUM_SSPS][SST_MAX_SSP_MUX][SST_MAX_SSP_DOMAINS] = { ++ [SST_SSP0] = { ++ [SST_SSP_MODEM_MUX] = { ++ [SST_SSP_MODEM_DOMAIN] = { ++ .ssp_id = SSP_MODEM, ++ .bits_per_slot = 16, ++ .slots = 1, ++ .ssp_mode = SSP_MODE_MASTER, ++ .pcm_mode = SSP_PCM_MODE_NETWORK, ++ .duplex = SSP_DUPLEX, ++ .ssp_protocol = SSP_MODE_PCM, ++ .fs_width = 1, ++ .fs_frequency = SSP_FS_48_KHZ, ++ .active_slot_map = 0x1, ++ .start_delay = 1, ++ }, ++ }, ++ }, ++ [SST_SSP1] = { ++ [SST_SSP_FM_MUX] = { ++ [SST_SSP_FM_DOMAIN] = { ++ .ssp_id = SSP_FM, ++ .bits_per_slot = 16, ++ .slots = 2, ++ .ssp_mode = SSP_MODE_MASTER, ++ .pcm_mode = SSP_PCM_MODE_NORMAL, ++ .duplex = SSP_DUPLEX, ++ .ssp_protocol = SSP_MODE_I2S, ++ .fs_width = 32, ++ .fs_frequency = SSP_FS_48_KHZ, ++ .active_slot_map = 0x3, ++ .start_delay = 0, ++ }, ++ }, ++ [SST_SSP_BT_MUX] = { ++ [SST_SSP_BT_NB_DOMAIN] = { ++ .ssp_id = SSP_BT, ++ .bits_per_slot = 16, ++ .slots = 1, ++ .ssp_mode = SSP_MODE_MASTER, ++ .pcm_mode = SSP_PCM_MODE_NORMAL, ++ .duplex = SSP_DUPLEX, ++ .ssp_protocol = SSP_MODE_PCM, ++ .fs_width = 1, ++ .fs_frequency = SSP_FS_8_KHZ, ++ .active_slot_map = 0x1, ++ .start_delay = 1, ++ }, ++ [SST_SSP_BT_WB_DOMAIN] = { ++ .ssp_id = SSP_BT, ++ .bits_per_slot = 16, ++ .slots = 1, ++ .ssp_mode = SSP_MODE_MASTER, ++ .pcm_mode = SSP_PCM_MODE_NORMAL, ++ .duplex = SSP_DUPLEX, ++ .ssp_protocol = SSP_MODE_PCM, ++ .fs_width = 1, ++ .fs_frequency = SSP_FS_16_KHZ, ++ .active_slot_map = 0x1, ++ .start_delay = 1, ++ }, ++ }, ++ }, ++ [SST_SSP2] = { ++ [SST_SSP_CODEC_MUX] = { ++ [SST_SSP_CODEC_DOMAIN] = { ++ .ssp_id = SSP_CODEC, ++ .bits_per_slot = 24, ++ .slots = 4, ++ .ssp_mode = SSP_MODE_MASTER, ++ .pcm_mode = SSP_PCM_MODE_NETWORK, ++ .duplex = SSP_DUPLEX, ++ .ssp_protocol = SSP_MODE_PCM, ++ .fs_width = 1, ++ .fs_frequency = SSP_FS_48_KHZ, ++ .active_slot_map = 0xF, ++ .start_delay = 0, ++ }, ++ }, ++ }, ++}; ++ ++#define SST_SSP_CFG(wssp_no) \ ++ (const struct sst_ssp_cfg){ .ssp_config = &sst_ssp_configs[wssp_no], \ ++ .ssp_number = wssp_no, \ ++ .mux_shift = &sst_ssp_mux_shift[wssp_no], \ ++ .domain_shift = &sst_ssp_domain_shift[wssp_no], } ++ ++static const struct snd_soc_dapm_widget sst_dapm_widgets[] = { ++ SND_SOC_DAPM_INPUT("tone"), ++ SND_SOC_DAPM_OUTPUT("aware"), ++ SND_SOC_DAPM_OUTPUT("vad"), ++ SST_SSP_INPUT("modem_in", sst_ssp_event, SST_SSP_CFG(SST_SSP0)), ++ SST_SSP_AIF_IN("codec_in0", sst_ssp_event, SST_SSP_CFG(SST_SSP2)), ++ SST_SSP_AIF_IN("codec_in1", sst_ssp_event, SST_SSP_CFG(SST_SSP2)), ++ SST_SSP_INPUT("bt_fm_in", sst_ssp_event, SST_SSP_CFG(SST_SSP1)), ++ SST_SSP_OUTPUT("modem_out", sst_ssp_event, SST_SSP_CFG(SST_SSP0)), ++ SST_SSP_AIF_OUT("codec_out0", sst_ssp_event, SST_SSP_CFG(SST_SSP2)), ++ SST_SSP_AIF_OUT("codec_out1", sst_ssp_event, SST_SSP_CFG(SST_SSP2)), ++ SST_SSP_OUTPUT("bt_fm_out", sst_ssp_event, SST_SSP_CFG(SST_SSP1)), ++ ++ /* Media Paths */ ++ /* MediaX IN paths are set via ALLOC, so no SET_MEDIA_PATH command */ ++ SST_PATH_INPUT("media0_in", SST_TASK_MMX, SST_SWM_IN_MEDIA0, NULL), ++ SST_PATH_INPUT("media1_in", SST_TASK_MMX, SST_SWM_IN_MEDIA1, NULL), ++ SST_PATH_INPUT("media2_in", SST_TASK_MMX, SST_SWM_IN_MEDIA2, sst_set_media_path), ++ SST_PATH_INPUT("media3_in", SST_TASK_MMX, SST_SWM_IN_MEDIA3, NULL), ++ SST_PATH_OUTPUT("media0_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA0, sst_set_media_path), ++ SST_PATH_OUTPUT("media1_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA1, sst_set_media_path), ++ ++ /* SBA PCM Paths */ ++ SST_PATH_INPUT("pcm0_in", SST_TASK_SBA, SST_SWM_IN_PCM0, sst_set_media_path), ++ SST_PATH_INPUT("pcm1_in", SST_TASK_SBA, SST_SWM_IN_PCM1, sst_set_media_path), ++ SST_PATH_OUTPUT("pcm0_out", SST_TASK_SBA, SST_SWM_OUT_PCM0, sst_set_media_path), ++ SST_PATH_OUTPUT("pcm1_out", SST_TASK_SBA, SST_SWM_OUT_PCM1, sst_set_media_path), ++ SST_PATH_OUTPUT("pcm2_out", SST_TASK_SBA, SST_SWM_OUT_PCM2, sst_set_media_path), ++ /* TODO: check if this needs SET_MEDIA_PATH command*/ ++ SST_PATH_INPUT("low_pcm0_in", SST_TASK_SBA, SST_SWM_IN_LOW_PCM0, NULL), ++ ++ SST_PATH_INPUT("voip_in", SST_TASK_SBA, SST_SWM_IN_VOIP, sst_set_media_path), ++ SST_PATH_OUTPUT("voip_out", SST_TASK_SBA, SST_SWM_OUT_VOIP, sst_set_media_path), ++ SST_PATH_OUTPUT("aware_out", SST_TASK_SBA, SST_SWM_OUT_AWARE, sst_set_media_path), ++ SST_PATH_OUTPUT("vad_out", SST_TASK_SBA, SST_SWM_OUT_VAD, sst_set_media_path), ++ ++ /* SBA Loops */ ++ SST_PATH_INPUT("sprot_loop_in", SST_TASK_SBA, SST_SWM_IN_SPROT_LOOP, NULL), ++ SST_PATH_INPUT("media_loop1_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP1, NULL), ++ SST_PATH_INPUT("media_loop2_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP2, NULL), ++ SST_PATH_MEDIA_LOOP_OUTPUT("sprot_loop_out", SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP, SST_FMT_MONO, sst_set_media_loop), ++ SST_PATH_MEDIA_LOOP_OUTPUT("media_loop1_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, SST_FMT_MONO, sst_set_media_loop), ++ SST_PATH_MEDIA_LOOP_OUTPUT("media_loop2_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2, SST_FMT_STEREO, sst_set_media_loop), ++ ++ /* TODO: need to send command */ ++ SST_PATH_INPUT("sidetone_in", SST_TASK_SBA, SST_SWM_IN_SIDETONE, NULL), ++ SST_PATH_INPUT("tone_in", SST_TASK_SBA, SST_SWM_IN_TONE, NULL), ++ SST_PATH_INPUT("bt_in", SST_TASK_SBA, SST_SWM_IN_BT, NULL), ++ SST_PATH_INPUT("fm_in", SST_TASK_SBA, SST_SWM_IN_FM, NULL), ++ SST_PATH_OUTPUT("bt_out", SST_TASK_SBA, SST_SWM_OUT_BT, NULL), ++ SST_PATH_OUTPUT("fm_out", SST_TASK_SBA, SST_SWM_OUT_FM, NULL), ++ ++ /* SBA Voice Paths */ ++ SST_PATH_INPUT("speech_in", SST_TASK_SBA, SST_SWM_IN_SPEECH, sst_set_speech_path), ++ SST_PATH_INPUT("txspeech_in", SST_TASK_SBA, SST_SWM_IN_TXSPEECH, sst_set_speech_path), ++ SST_PATH_OUTPUT("hf_sns_out", SST_TASK_SBA, SST_SWM_OUT_HF_SNS, sst_set_speech_path), ++ SST_PATH_OUTPUT("hf_out", SST_TASK_SBA, SST_SWM_OUT_HF, sst_set_speech_path), ++ SST_PATH_OUTPUT("speech_out", SST_TASK_SBA, SST_SWM_OUT_SPEECH, sst_set_speech_path), ++ SST_PATH_OUTPUT("rxspeech_out", SST_TASK_SBA, SST_SWM_OUT_RXSPEECH, sst_set_speech_path), ++ ++ /* Media Mixers */ ++ SST_SWM_MIXER("media0_out mix 0", SST_MIX_MEDIA0, SST_TASK_MMX, SST_SWM_OUT_MEDIA0, ++ sst_mix_media0_controls, sst_swm_mixer_event), ++ SST_SWM_MIXER("media1_out mix 0", SST_MIX_MEDIA1, SST_TASK_MMX, SST_SWM_OUT_MEDIA1, ++ sst_mix_media1_controls, sst_swm_mixer_event), ++ ++ /* SBA PCM mixers */ ++ SST_SWM_MIXER("pcm0_out mix 0", SST_MIX_PCM0, SST_TASK_SBA, SST_SWM_OUT_PCM0, ++ sst_mix_pcm0_controls, sst_swm_mixer_event), ++ SST_SWM_MIXER("pcm1_out mix 0", SST_MIX_PCM1, SST_TASK_SBA, SST_SWM_OUT_PCM1, ++ sst_mix_pcm1_controls, sst_swm_mixer_event), ++ SST_SWM_MIXER("pcm2_out mix 0", SST_MIX_PCM2, SST_TASK_SBA, SST_SWM_OUT_PCM2, ++ sst_mix_pcm2_controls, sst_swm_mixer_event), ++ ++ /* SBA Loop mixers */ ++ SST_SWM_MIXER("sprot_loop_out mix 0", SST_MIX_LOOP0, SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP, ++ sst_mix_sprot_l0_controls, sst_swm_mixer_event), ++ SST_SWM_MIXER("media_loop1_out mix 0", SST_MIX_LOOP1, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, ++ sst_mix_media_l1_controls, sst_swm_mixer_event), ++ SST_SWM_MIXER("media_loop2_out mix 0", SST_MIX_LOOP2, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2, ++ sst_mix_media_l2_controls, sst_swm_mixer_event), ++ ++ SST_SWM_MIXER("voip_out mix 0", SST_MIX_VOIP, SST_TASK_SBA, SST_SWM_OUT_VOIP, ++ sst_mix_voip_controls, sst_swm_mixer_event), ++ SST_SWM_MIXER("aware_out mix 0", SST_MIX_AWARE, SST_TASK_SBA, SST_SWM_OUT_AWARE, ++ sst_mix_aware_controls, sst_swm_mixer_event), ++ SST_SWM_MIXER("vad_out mix 0", SST_MIX_VAD, SST_TASK_SBA, SST_SWM_OUT_VAD, ++ sst_mix_vad_controls, sst_swm_mixer_event), ++ ++ /* SBA Voice mixers */ ++ SST_SWM_MIXER("hf_sns_out mix 0", SST_MIX_HF_SNS, SST_TASK_SBA, SST_SWM_OUT_HF_SNS, ++ sst_mix_hf_sns_controls, sst_swm_mixer_event), ++ SST_SWM_MIXER("hf_out mix 0", SST_MIX_HF, SST_TASK_SBA, SST_SWM_OUT_HF, ++ sst_mix_hf_controls, sst_swm_mixer_event), ++ SST_SWM_MIXER("speech_out mix 0", SST_MIX_SPEECH, SST_TASK_SBA, SST_SWM_OUT_SPEECH, ++ sst_mix_speech_controls, sst_swm_mixer_event), ++ SST_SWM_MIXER("rxspeech_out mix 0", SST_MIX_RXSPEECH, SST_TASK_SBA, SST_SWM_OUT_RXSPEECH, ++ sst_mix_rxspeech_controls, sst_swm_mixer_event), ++ ++ /* SBA Backend mixers */ ++ SST_SWM_MIXER("codec_out0 mix 0", SST_MIX_CODEC0, SST_TASK_SBA, SST_SWM_OUT_CODEC0, ++ sst_mix_codec0_controls, sst_swm_mixer_event), ++ SST_SWM_MIXER("codec_out1 mix 0", SST_MIX_CODEC1, SST_TASK_SBA, SST_SWM_OUT_CODEC1, ++ sst_mix_codec1_controls, sst_swm_mixer_event), ++ SST_SWM_MIXER("bt_out mix 0", SST_MIX_BT, SST_TASK_SBA, SST_SWM_OUT_BT, ++ sst_mix_bt_controls, sst_swm_mixer_event), ++ SST_SWM_MIXER("fm_out mix 0", SST_MIX_FM, SST_TASK_SBA, SST_SWM_OUT_FM, ++ sst_mix_fm_controls, sst_swm_mixer_event), ++ SST_SWM_MIXER("modem_out mix 0", SST_MIX_MODEM, SST_TASK_SBA, SST_SWM_OUT_MODEM, ++ sst_mix_modem_controls, sst_swm_mixer_event), ++ ++ SND_SOC_DAPM_SWITCH("aware_out aware 0", SND_SOC_NOPM, 0, 0, &sst_mix_sw_aware), ++ SND_SOC_DAPM_MUX("ssp1_out mux 0", SND_SOC_NOPM, 0, 0, &sst_bt_fm_mux), ++ ++ SND_SOC_DAPM_SUPPLY("VBTimer", SND_SOC_NOPM, 0, 0, ++ sst_vb_trigger_event, ++ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), ++}; ++ ++static const struct snd_soc_dapm_route intercon[] = { ++ {"media0_in", NULL, "Compress Playback"}, ++ {"media1_in", NULL, "Headset Playback"}, ++ {"media2_in", NULL, "pcm0_out"}, ++ {"media3_in", NULL, "Deepbuffer Playback"}, ++ ++ {"media0_out mix 0", "media0_in", "media0_in"}, ++ {"media0_out mix 0", "media1_in", "media1_in"}, ++ {"media0_out mix 0", "media2_in", "media2_in"}, ++ {"media0_out mix 0", "media3_in", "media3_in"}, ++ {"media1_out mix 0", "media0_in", "media0_in"}, ++ {"media1_out mix 0", "media1_in", "media1_in"}, ++ {"media1_out mix 0", "media2_in", "media2_in"}, ++ {"media1_out mix 0", "media3_in", "media3_in"}, ++ ++ {"media0_out", NULL, "media0_out mix 0"}, ++ {"media1_out", NULL, "media1_out mix 0"}, ++ {"pcm0_in", NULL, "media0_out"}, ++ {"pcm1_in", NULL, "media1_out"}, ++ ++ {"Headset Capture", NULL, "pcm1_out"}, ++ {"Headset Capture", NULL, "pcm2_out"}, ++ {"pcm0_out", NULL, "pcm0_out mix 0"}, ++ SST_SBA_MIXER_GRAPH_MAP("pcm0_out mix 0"), ++ {"pcm1_out", NULL, "pcm1_out mix 0"}, ++ SST_SBA_MIXER_GRAPH_MAP("pcm1_out mix 0"), ++ {"pcm2_out", NULL, "pcm2_out mix 0"}, ++ SST_SBA_MIXER_GRAPH_MAP("pcm2_out mix 0"), ++ ++ {"media_loop1_in", NULL, "media_loop1_out"}, ++ {"media_loop1_out", NULL, "media_loop1_out mix 0"}, ++ SST_SBA_MIXER_GRAPH_MAP("media_loop1_out mix 0"), ++ {"media_loop2_in", NULL, "media_loop2_out"}, ++ {"media_loop2_out", NULL, "media_loop2_out mix 0"}, ++ SST_SBA_MIXER_GRAPH_MAP("media_loop2_out mix 0"), ++ {"sprot_loop_in", NULL, "sprot_loop_out"}, ++ {"sprot_loop_out", NULL, "sprot_loop_out mix 0"}, ++ SST_SBA_MIXER_GRAPH_MAP("sprot_loop_out mix 0"), ++ ++ {"voip_in", NULL, "VOIP Playback"}, ++ {"VOIP Capture", NULL, "voip_out"}, ++ {"voip_out", NULL, "voip_out mix 0"}, ++ SST_SBA_MIXER_GRAPH_MAP("voip_out mix 0"), ++ ++ {"aware", NULL, "aware_out"}, ++ {"aware_out", NULL, "aware_out aware 0"}, ++ {"aware_out aware 0", "switch", "aware_out mix 0"}, ++ SST_SBA_MIXER_GRAPH_MAP("aware_out mix 0"), ++ {"vad", NULL, "vad_out"}, ++ {"vad_out", NULL, "vad_out mix 0"}, ++ SST_SBA_MIXER_GRAPH_MAP("vad_out mix 0"), ++ ++ {"codec_out0", NULL, "codec_out0 mix 0"}, ++ SST_SBA_MIXER_GRAPH_MAP("codec_out0 mix 0"), ++ {"codec_out1", NULL, "codec_out1 mix 0"}, ++ SST_SBA_MIXER_GRAPH_MAP("codec_out1 mix 0"), ++ {"modem_out", NULL, "modem_out mix 0"}, ++ SST_SBA_MIXER_GRAPH_MAP("modem_out mix 0"), ++ ++ {"bt_fm_out", NULL, "ssp1_out mux 0"}, ++ {"ssp1_out mux 0", "bt", "bt_out"}, ++ {"ssp1_out mux 0", "fm", "fm_out"}, ++ {"bt_out", NULL, "bt_out mix 0"}, ++ SST_SBA_MIXER_GRAPH_MAP("bt_out mix 0"), ++ {"fm_out", NULL, "fm_out mix 0"}, ++ SST_SBA_MIXER_GRAPH_MAP("fm_out mix 0"), ++ {"bt_in", NULL, "bt_fm_in"}, ++ {"fm_in", NULL, "bt_fm_in"}, ++ ++ /* Uplink processing */ ++ {"txspeech_in", NULL, "hf_sns_out"}, ++ {"txspeech_in", NULL, "hf_out"}, ++ {"txspeech_in", NULL, "speech_out"}, ++ ++ {"hf_sns_out", NULL, "hf_sns_out mix 0"}, ++ SST_SBA_MIXER_GRAPH_MAP("hf_sns_out mix 0"), ++ {"hf_out", NULL, "hf_out mix 0"}, ++ SST_SBA_MIXER_GRAPH_MAP("hf_out mix 0"), ++ {"speech_out", NULL, "speech_out mix 0"}, ++ SST_SBA_MIXER_GRAPH_MAP("speech_out mix 0"), ++ ++ /* Downlink processing */ ++ {"speech_in", NULL, "rxspeech_out"}, ++ {"rxspeech_out", NULL, "rxspeech_out mix 0"}, ++ SST_SBA_MIXER_GRAPH_MAP("rxspeech_out mix 0"), ++ ++ /* TODO: add Tone inputs */ ++ /* TODO: add Low Latency stream support */ ++ ++ {"Headset Capture", NULL, "VBTimer"}, ++ {"Headset Playback", NULL, "VBTimer"}, ++ {"Deepbuffer Playback", NULL, "VBTimer"}, ++ {"Compress Playback", NULL, "VBTimer"}, ++ {"VOIP Playback", NULL, "VBTimer"}, ++ {"aware", NULL, "VBTimer"}, ++ {"modem_in", NULL, "VBTimer"}, ++ {"modem_out", NULL, "VBTimer"}, ++ {"bt_fm_in", NULL, "VBTimer"}, ++ {"bt_fm_out", NULL, "VBTimer"}, ++}; ++ ++static const char * const sst_nb_wb_texts[] = { ++ "narrowband", "wideband", ++}; ++ ++static const struct snd_kcontrol_new sst_mux_controls[] = { ++ SST_SSP_MUX_CTL("domain voice mode", 0, SST_MUX_REG, SST_VOICE_MODE_SHIFT, sst_nb_wb_texts, ++ sst_mode_get, sst_mode_put), ++ SST_SSP_MUX_CTL("domain bt mode", 0, SST_MUX_REG, SST_BT_MODE_SHIFT, sst_nb_wb_texts, ++ sst_mode_get, sst_mode_put), ++}; ++ ++static const char * const slot_names[] = { ++ "none", ++ "slot 0", "slot 1", "slot 2", "slot 3", ++ "slot 4", "slot 5", "slot 6", "slot 7", /* not supported by FW */ ++}; ++ ++static const char * const channel_names[] = { ++ "none", ++ "codec_out0_0", "codec_out0_1", "codec_out1_0", "codec_out1_1", ++ "codec_out2_0", "codec_out2_1", "codec_out3_0", "codec_out3_1", /* not supported by FW */ ++}; ++ ++#define SST_INTERLEAVER(xpname, slot_name, slotno) \ ++ SST_SSP_SLOT_CTL(xpname, "interleaver", slot_name, slotno, 1, \ ++ channel_names, sst_slot_get, sst_slot_put) ++ ++#define SST_DEINTERLEAVER(xpname, channel_name, channel_no) \ ++ SST_SSP_SLOT_CTL(xpname, "deinterleaver", channel_name, channel_no, 0, \ ++ slot_names, sst_slot_get, sst_slot_put) ++ ++static const struct snd_kcontrol_new sst_slot_controls[] = { ++ SST_INTERLEAVER("codec_out", "slot 0", 0), ++ SST_INTERLEAVER("codec_out", "slot 1", 1), ++ SST_INTERLEAVER("codec_out", "slot 2", 2), ++ SST_INTERLEAVER("codec_out", "slot 3", 3), ++ SST_DEINTERLEAVER("codec_in", "codec_in0_0", 0), ++ SST_DEINTERLEAVER("codec_in", "codec_in0_1", 1), ++ SST_DEINTERLEAVER("codec_in", "codec_in1_0", 2), ++ SST_DEINTERLEAVER("codec_in", "codec_in1_1", 3), ++}; ++ ++#define SST_NUM_PROBE_CONNECTION_PTS 31 ++static const struct sst_probe_config sst_probes[SST_NUM_PROBE_CONNECTION_PTS] = { ++ /* TODO: get this struct from FW config data */ ++ /* TODO: only gain outputs supported currently */ ++ { "media0_in gain", SST_PATH_INDEX_MEDIA0_IN, SST_MODULE_ID_GAIN_CELL, SST_TASK_MMX, { 1, 2, 1 } }, ++ { "media1_in gain", SST_PATH_INDEX_MEDIA1_IN, SST_MODULE_ID_GAIN_CELL, SST_TASK_MMX, { 1, 2, 1 } }, ++ { "media2_in gain", SST_PATH_INDEX_MEDIA2_IN, SST_MODULE_ID_GAIN_CELL, SST_TASK_MMX, { 1, 2, 1 } }, ++ { "media3_in gain", SST_PATH_INDEX_MEDIA3_IN, SST_MODULE_ID_GAIN_CELL, SST_TASK_MMX, { 1, 2, 1 } }, ++ { "pcm0_in gain", SST_PATH_INDEX_PCM0_IN, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, ++ { "pcm1_in gain", SST_PATH_INDEX_PCM1_IN, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, ++ { "pcm1_out gain", SST_PATH_INDEX_PCM1_OUT, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, ++ { "pcm2_out gain", SST_PATH_INDEX_PCM2_OUT, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, ++ { "voip_in gain", SST_PATH_INDEX_VOIP_IN, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, ++ { "voip_out gain", SST_PATH_INDEX_VOIP_OUT, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, ++ { "aware_out gain", SST_PATH_INDEX_AWARE_OUT, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, ++ { "vad_out gain", SST_PATH_INDEX_VAD_OUT, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, ++ { "hf_sns_out gain", SST_PATH_INDEX_HF_SNS_OUT, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, ++ { "hf_out gain", SST_PATH_INDEX_HF_OUT, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, ++ { "speech_out gain", SST_PATH_INDEX_SPEECH_OUT, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, ++ { "txspeech_in gain", SST_PATH_INDEX_TX_SPEECH_IN, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, ++ { "rxspeech_out gain", SST_PATH_INDEX_RX_SPEECH_OUT, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, ++ { "speech_in gain", SST_PATH_INDEX_SPEECH_IN, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, ++ { "media_loop1_out gain", SST_PATH_INDEX_MEDIA_LOOP1_OUT , SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, ++ { "media_loop2_out gain", SST_PATH_INDEX_MEDIA_LOOP2_OUT , SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, ++ { "tone_in gain", SST_PATH_INDEX_TONE_IN, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, ++ { "codec_out0 gain", SST_PATH_INDEX_CODEC_OUT0, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, ++ { "codec_out1 gain", SST_PATH_INDEX_CODEC_OUT1, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, ++ { "bt_out gain", SST_PATH_INDEX_BT_OUT, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, ++ { "fm_out gain", SST_PATH_INDEX_FM_OUT, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, ++ { "modem_out gain", SST_PATH_INDEX_MODEM_OUT, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, ++ { "codec_in0 gain", SST_PATH_INDEX_CODEC_IN0, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, ++ { "codec_in1 gain", SST_PATH_INDEX_CODEC_IN1, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, ++ { "bt_in gain", SST_PATH_INDEX_BT_IN, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, ++ { "fm_in gain", SST_PATH_INDEX_FM_IN, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, ++ { "modem_in gain", SST_PATH_INDEX_MODEM_IN, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, ++}; ++ ++/* initialized based on names in sst_probes array */ ++static const char *sst_probe_enum_texts[SST_NUM_PROBE_CONNECTION_PTS]; ++static const SOC_ENUM_SINGLE_EXT_DECL(sst_probe_enum, sst_probe_enum_texts); ++ ++#define SST_PROBE_CTL(name, num) \ ++ SST_PROBE_ENUM(SST_PROBE_CTL_NAME(name, num, "connection"), \ ++ sst_probe_enum, sst_probe_get, sst_probe_put) ++ /* TODO: implement probe gains ++ SOC_SINGLE_EXT_TLV(SST_PROBE_CTL_NAME(name, num, "gains"), xreg, xshift, ++ xmax, xinv, xget, xput, sst_gain_tlv_common) ++ */ ++ ++static const struct snd_kcontrol_new sst_probe_controls[] = { ++ SST_PROBE_CTL("probe out", 0), ++ SST_PROBE_CTL("probe out", 1), ++ SST_PROBE_CTL("probe out", 2), ++ SST_PROBE_CTL("probe out", 3), ++ SST_PROBE_CTL("probe out", 4), ++ SST_PROBE_CTL("probe out", 5), ++ SST_PROBE_CTL("probe out", 6), ++ SST_PROBE_CTL("probe out", 7), ++ SST_PROBE_CTL("probe in", 0), ++ SST_PROBE_CTL("probe in", 1), ++ SST_PROBE_CTL("probe in", 2), ++ SST_PROBE_CTL("probe in", 3), ++ SST_PROBE_CTL("probe in", 4), ++ SST_PROBE_CTL("probe in", 5), ++ SST_PROBE_CTL("probe in", 6), ++ SST_PROBE_CTL("probe in", 7), ++}; ++ ++/* Gain helper with min/max set */ ++#define SST_GAIN(name, path_id, task_id, instance, gain_var) \ ++ SST_GAIN_KCONTROLS(name, "gain", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE, \ ++ SST_GAIN_TC_MIN, SST_GAIN_TC_MAX, \ ++ sst_gain_get, sst_gain_put, \ ++ SST_MODULE_ID_GAIN_CELL, path_id, instance, task_id, \ ++ sst_gain_tlv_common, gain_var) ++ ++#define SST_VOLUME(name, path_id, task_id, instance, gain_var) \ ++ SST_GAIN_KCONTROLS(name, "volume", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE, \ ++ SST_GAIN_TC_MIN, SST_GAIN_TC_MAX, \ ++ sst_gain_get, sst_gain_put, \ ++ SST_MODULE_ID_VOLUME, path_id, instance, task_id, \ ++ sst_gain_tlv_common, gain_var) ++ ++#define SST_NUM_GAINS 36 ++static struct sst_gain_value sst_gains[SST_NUM_GAINS]; ++ ++static const struct snd_kcontrol_new sst_gain_controls[] = { ++ SST_GAIN("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[0]), ++ SST_GAIN("media1_in", SST_PATH_INDEX_MEDIA1_IN, SST_TASK_MMX, 0, &sst_gains[1]), ++ SST_GAIN("media2_in", SST_PATH_INDEX_MEDIA2_IN, SST_TASK_MMX, 0, &sst_gains[2]), ++ SST_GAIN("media3_in", SST_PATH_INDEX_MEDIA3_IN, SST_TASK_MMX, 0, &sst_gains[3]), ++ ++ SST_GAIN("pcm0_in", SST_PATH_INDEX_PCM0_IN, SST_TASK_SBA, 0, &sst_gains[4]), ++ SST_GAIN("pcm1_in", SST_PATH_INDEX_PCM1_IN, SST_TASK_SBA, 0, &sst_gains[5]), ++ SST_GAIN("low_pcm0_in", SST_PATH_INDEX_LOW_PCM0_IN, SST_TASK_SBA, 0, &sst_gains[6]), ++ SST_GAIN("pcm1_out", SST_PATH_INDEX_PCM1_OUT, SST_TASK_SBA, 0, &sst_gains[7]), ++ SST_GAIN("pcm2_out", SST_PATH_INDEX_PCM1_OUT, SST_TASK_SBA, 0, &sst_gains[8]), ++ ++ SST_GAIN("voip_in", SST_PATH_INDEX_VOIP_IN, SST_TASK_SBA, 0, &sst_gains[9]), ++ SST_GAIN("voip_out", SST_PATH_INDEX_VOIP_OUT, SST_TASK_SBA, 0, &sst_gains[10]), ++ SST_GAIN("tone_in", SST_PATH_INDEX_TONE_IN, SST_TASK_SBA, 0, &sst_gains[11]), ++ ++ SST_GAIN("aware_out", SST_PATH_INDEX_AWARE_OUT, SST_TASK_SBA, 0, &sst_gains[12]), ++ SST_GAIN("vad_out", SST_PATH_INDEX_VAD_OUT, SST_TASK_SBA, 0, &sst_gains[13]), ++ ++ SST_GAIN("hf_sns_out", SST_PATH_INDEX_HF_SNS_OUT, SST_TASK_SBA, 0, &sst_gains[14]), ++ SST_GAIN("hf_out", SST_PATH_INDEX_HF_OUT, SST_TASK_SBA, 0, &sst_gains[15]), ++ SST_GAIN("speech_out", SST_PATH_INDEX_SPEECH_OUT, SST_TASK_SBA, 0, &sst_gains[16]), ++ SST_GAIN("txspeech_in", SST_PATH_INDEX_TX_SPEECH_IN, SST_TASK_SBA, 0, &sst_gains[17]), ++ SST_GAIN("rxspeech_out", SST_PATH_INDEX_RX_SPEECH_OUT, SST_TASK_SBA, 0, &sst_gains[18]), ++ SST_GAIN("speech_in", SST_PATH_INDEX_SPEECH_IN, SST_TASK_SBA, 0, &sst_gains[19]), ++ ++ SST_GAIN("codec_in0", SST_PATH_INDEX_CODEC_IN0, SST_TASK_SBA, 0, &sst_gains[20]), ++ SST_GAIN("codec_in1", SST_PATH_INDEX_CODEC_IN1, SST_TASK_SBA, 0, &sst_gains[21]), ++ SST_GAIN("codec_out0", SST_PATH_INDEX_CODEC_OUT0, SST_TASK_SBA, 0, &sst_gains[22]), ++ SST_GAIN("codec_out1", SST_PATH_INDEX_CODEC_OUT1, SST_TASK_SBA, 0, &sst_gains[23]), ++ SST_GAIN("bt_out", SST_PATH_INDEX_BT_OUT, SST_TASK_SBA, 0, &sst_gains[24]), ++ SST_GAIN("fm_out", SST_PATH_INDEX_FM_OUT, SST_TASK_SBA, 0, &sst_gains[25]), ++ SST_GAIN("bt_in", SST_PATH_INDEX_BT_IN, SST_TASK_SBA, 0, &sst_gains[26]), ++ SST_GAIN("fm_in", SST_PATH_INDEX_FM_IN, SST_TASK_SBA, 0, &sst_gains[27]), ++ SST_GAIN("modem_in", SST_PATH_INDEX_MODEM_IN, SST_TASK_SBA, 0, &sst_gains[28]), ++ SST_GAIN("modem_out", SST_PATH_INDEX_MODEM_OUT, SST_TASK_SBA, 0, &sst_gains[29]), ++ SST_GAIN("media_loop1_out", SST_PATH_INDEX_MEDIA_LOOP1_OUT, SST_TASK_SBA, 0, &sst_gains[30]), ++ SST_GAIN("media_loop2_out", SST_PATH_INDEX_MEDIA_LOOP2_OUT, SST_TASK_SBA, 0, &sst_gains[31]), ++ SST_GAIN("sprot_loop_out", SST_PATH_INDEX_SPROT_LOOP_OUT, SST_TASK_SBA, 0, &sst_gains[32]), ++ SST_VOLUME("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[33]), ++ SST_GAIN("sidetone_in", SST_PATH_INDEX_SIDETONE_IN, SST_TASK_SBA, 0, &sst_gains[34]), ++ SST_GAIN("speech_out", SST_PATH_INDEX_SPEECH_OUT, SST_TASK_FBA_UL, 1, &sst_gains[35]), ++}; ++ ++static const struct snd_kcontrol_new sst_algo_controls[] = { ++ SST_ALGO_KCONTROL_BYTES("media_loop1_out", "fir", 138, SST_MODULE_ID_FIR_24, ++ SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR), ++ SST_ALGO_KCONTROL_BYTES("media_loop1_out", "iir", 300, SST_MODULE_ID_IIR_24, ++ SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_IIR), ++ SST_ALGO_KCONTROL_BYTES("media_loop1_out", "mdrp", 76, SST_MODULE_ID_MDRP, ++ SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_SET_MDRP), ++ SST_ALGO_KCONTROL_BYTES("media_loop2_out", "fir", 272, SST_MODULE_ID_FIR_24, ++ SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR), ++ SST_ALGO_KCONTROL_BYTES("media_loop2_out", "iir", 300, SST_MODULE_ID_IIR_24, ++ SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_VB_SET_IIR), ++ SST_ALGO_KCONTROL_BYTES("media_loop2_out", "mdrp", 76, SST_MODULE_ID_MDRP, ++ SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_SET_MDRP), ++ SST_ALGO_KCONTROL_BYTES("aware_out", "fir", 272, SST_MODULE_ID_FIR_24, ++ SST_PATH_INDEX_AWARE_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR), ++ SST_ALGO_KCONTROL_BYTES("aware_out", "iir", 300, SST_MODULE_ID_IIR_24, ++ SST_PATH_INDEX_AWARE_OUT, 0, SST_TASK_SBA, SBA_VB_SET_IIR), ++ SST_ALGO_KCONTROL_BYTES("aware_out", "aware", 48, SST_MODULE_ID_CONTEXT_ALGO_AWARE, ++ SST_PATH_INDEX_AWARE_OUT, 0, SST_TASK_AWARE, AWARE_ENV_CLASS_PARAMS), ++ SST_ALGO_KCONTROL_BYTES("vad_out", "fir", 272, SST_MODULE_ID_FIR_24, ++ SST_PATH_INDEX_VAD_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR), ++ SST_ALGO_KCONTROL_BYTES("vad_out", "iir", 300, SST_MODULE_ID_IIR_24, ++ SST_PATH_INDEX_VAD_OUT, 0, SST_TASK_SBA, SBA_VB_SET_IIR), ++ SST_ALGO_KCONTROL_BYTES("sprot_loop_out", "lpro", 192, SST_MODULE_ID_SPROT, ++ SST_PATH_INDEX_SPROT_LOOP_OUT, 0, SST_TASK_SBA, SBA_VB_LPRO), ++ SST_ALGO_KCONTROL_BYTES("codec_in0", "dcr", 300, SST_MODULE_ID_FILT_DCR, ++ SST_PATH_INDEX_CODEC_IN0, 0, SST_TASK_SBA, SBA_VB_SET_IIR), ++ SST_ALGO_KCONTROL_BYTES("codec_in1", "dcr", 300, SST_MODULE_ID_FILT_DCR, ++ SST_PATH_INDEX_CODEC_IN1, 0, SST_TASK_SBA, SBA_VB_SET_IIR), ++ /* Uplink */ ++ SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "fir_speech", 136, SST_MODULE_ID_FIR_16, ++ SST_PATH_INDEX_SPEECH_OUT, 0, SST_TASK_FBA_UL, FBA_VB_SET_FIR), ++ SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "fir_hf_sns", 136, SST_MODULE_ID_FIR_16, ++ SST_PATH_INDEX_HF_SNS_OUT, 0, SST_TASK_FBA_UL, FBA_VB_SET_FIR), ++ SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "iir_speech", 48, SST_MODULE_ID_IIR_16, ++ SST_PATH_INDEX_SPEECH_OUT, 0, SST_TASK_FBA_UL, FBA_VB_SET_IIR), ++ SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "iir_hf_sns", 48, SST_MODULE_ID_IIR_16, ++ SST_PATH_INDEX_HF_SNS_OUT, 0, SST_TASK_FBA_UL, FBA_VB_SET_IIR), ++ SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "aec", 640, SST_MODULE_ID_AEC, ++ SST_PATH_INDEX_SPEECH_OUT, 0, SST_TASK_FBA_UL, FBA_VB_AEC), ++ SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "nr", 38, SST_MODULE_ID_NR, ++ SST_PATH_INDEX_SPEECH_OUT, 0, SST_TASK_FBA_UL, FBA_VB_NR_UL), ++ SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "agc", 58, SST_MODULE_ID_AGC, ++ SST_PATH_INDEX_SPEECH_OUT, 0, SST_TASK_FBA_UL, FBA_VB_AGC), ++ SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "biquad", 22, SST_MODULE_ID_DRP, ++ SST_PATH_INDEX_SPEECH_OUT, 0, SST_TASK_FBA_UL, FBA_VB_SET_BIQUAD_D_C), ++ SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "compr", 36, SST_MODULE_ID_DRP, ++ SST_PATH_INDEX_SPEECH_OUT, 0, SST_TASK_FBA_UL, FBA_VB_DUAL_BAND_COMP), ++ SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "sns", 324, SST_MODULE_ID_NR_SNS, ++ SST_PATH_INDEX_SPEECH_OUT, 0, SST_TASK_FBA_UL, FBA_VB_SNS), ++ SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "ser", 42, SST_MODULE_ID_SER, ++ SST_PATH_INDEX_SPEECH_OUT, 0, SST_TASK_FBA_UL, FBA_VB_SER), ++ SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "cni", 48, SST_MODULE_ID_CNI_TX, ++ SST_PATH_INDEX_SPEECH_OUT, 0, SST_TASK_FBA_UL, FBA_VB_TX_CNI), ++ SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "ref", 24, SST_MODULE_ID_REF_LINE, ++ SST_PATH_INDEX_HF_OUT, 0, SST_TASK_FBA_UL, FBA_VB_SET_REF_LINE), ++ SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "delay", 6, SST_MODULE_ID_EDL, ++ SST_PATH_INDEX_HF_OUT, 0, SST_TASK_FBA_UL, FBA_VB_SET_DELAY_LINE), ++ SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "bmf", 264, SST_MODULE_ID_BMF, ++ SST_PATH_INDEX_HF_SNS_OUT, 0, SST_TASK_FBA_UL, FBA_VB_BMF), ++ SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "dnr", 18, SST_MODULE_ID_DNR, ++ SST_PATH_INDEX_SPEECH_OUT, 0, SST_TASK_FBA_UL, FBA_VB_DNR), ++ /* Downlink */ ++ SST_COMBO_ALGO_KCONTROL_BYTES("speech_in", "dl_module", "ana", 52, SST_MODULE_ID_ANA, ++ SST_PATH_INDEX_SPEECH_IN, 0, SST_TASK_FBA_DL, FBA_VB_ANA), ++ SST_COMBO_ALGO_KCONTROL_BYTES("speech_in", "dl_module", "fir", 136, SST_MODULE_ID_FIR_16, ++ SST_PATH_INDEX_SPEECH_IN, 0, SST_TASK_FBA_DL, FBA_VB_SET_FIR), ++ SST_COMBO_ALGO_KCONTROL_BYTES("speech_in", "dl_module", "iir", 48, SST_MODULE_ID_IIR_16, ++ SST_PATH_INDEX_SPEECH_IN, 0, SST_TASK_FBA_DL, FBA_VB_SET_IIR), ++ SST_COMBO_ALGO_KCONTROL_BYTES("speech_in", "dl_module", "nr", 38, SST_MODULE_ID_NR, ++ SST_PATH_INDEX_SPEECH_IN, 0, SST_TASK_FBA_DL, FBA_VB_NR_DL), ++ SST_COMBO_ALGO_KCONTROL_BYTES("speech_in", "dl_module", "biquad", 22, SST_MODULE_ID_DRP, ++ SST_PATH_INDEX_SPEECH_IN, 0, SST_TASK_FBA_DL, FBA_VB_SET_BIQUAD_D_C), ++ SST_COMBO_ALGO_KCONTROL_BYTES("speech_in", "dl_module", "compr", 36, SST_MODULE_ID_DRP, ++ SST_PATH_INDEX_SPEECH_IN, 0, SST_TASK_FBA_DL, FBA_VB_DUAL_BAND_COMP), ++ SST_COMBO_ALGO_KCONTROL_BYTES("speech_in", "dl_module", "cni", 48, SST_MODULE_ID_CNI, ++ SST_PATH_INDEX_SPEECH_IN, 0, SST_TASK_FBA_DL, FBA_VB_RX_CNI), ++ SST_COMBO_ALGO_KCONTROL_BYTES("speech_in", "dl_module", "bwx", 54, SST_MODULE_ID_BWX, ++ SST_PATH_INDEX_SPEECH_IN, 0, SST_TASK_FBA_DL, FBA_VB_BWX), ++ SST_COMBO_ALGO_KCONTROL_BYTES("speech_in", "dl_module", "gmm", 586, SST_MODULE_ID_BWX, ++ SST_PATH_INDEX_SPEECH_IN, 0, SST_TASK_FBA_DL, FBA_VB_GMM), ++ SST_COMBO_ALGO_KCONTROL_BYTES("speech_in", "dl_module", "glc", 18, SST_MODULE_ID_GLC, ++ SST_PATH_INDEX_SPEECH_IN, 0, SST_TASK_FBA_DL, FBA_VB_GLC), ++}; ++ ++static const struct snd_kcontrol_new sst_debug_controls[] = { ++ SND_SOC_BYTES_EXT("sst debug byte control", SST_MAX_BIN_BYTES, ++ sst_byte_control_get, sst_byte_control_set), ++}; ++ ++static inline bool is_sst_dapm_widget(struct snd_soc_dapm_widget *w) ++{ ++ if ((w->id == snd_soc_dapm_pga) || ++ (w->id == snd_soc_dapm_aif_in) || ++ (w->id == snd_soc_dapm_aif_out) || ++ (w->id == snd_soc_dapm_input) || ++ (w->id == snd_soc_dapm_output) || ++ (w->id == snd_soc_dapm_mixer)) ++ return true; ++ else ++ return false; ++} ++ ++int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute) ++{ ++ struct snd_soc_platform *platform = dai->platform; ++ struct sst_data *sst = snd_soc_platform_get_drvdata(platform); ++ struct snd_soc_dapm_widget *w; ++ struct snd_soc_dapm_path *p = NULL; ++ ++ pr_debug("%s: enter, dai-name=%s dir=%d\n", __func__, dai->name, stream); ++ ++ if (stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ pr_debug("Stream name=%s\n", dai->playback_widget->name); ++ w = dai->playback_widget; ++ list_for_each_entry(p, &w->sinks, list_source) { ++ if (p->connected && !p->connected(w, p->sink)) ++ continue; ++ ++ if (p->connect && p->sink->power && is_sst_dapm_widget(p->sink)) { ++ struct sst_ids *ids = p->sink->priv; ++ ++ pr_debug("send gains for widget=%s\n", p->sink->name); ++ sst_set_pipe_gain(ids, sst, mute); ++ } ++ } ++ } else { ++ pr_debug("Stream name=%s\n", dai->capture_widget->name); ++ w = dai->capture_widget; ++ list_for_each_entry(p, &w->sources, list_sink) { ++ if (p->connected && !p->connected(w, p->sink)) ++ continue; ++ ++ if (p->connect && p->source->power && is_sst_dapm_widget(p->source)) { ++ struct sst_ids *ids = p->source->priv; ++ ++ pr_debug("send gain for widget=%s\n", p->source->name); ++ sst_set_pipe_gain(ids, sst, mute); ++ } ++ } ++ } ++ return 0; ++} ++ ++static int sst_fill_module_list(struct snd_kcontrol *kctl, ++ struct snd_soc_dapm_widget *w, int type) ++{ ++ struct module *module = NULL; ++ struct sst_ids *ids = w->priv; ++ ++ module = devm_kzalloc(w->platform->dev, sizeof(*module), GFP_KERNEL); ++ if (!module) { ++ pr_err("kzalloc block failed\n"); ++ return -ENOMEM; ++ } ++ ++ if (type == SST_MODULE_GAIN) { ++ struct sst_gain_mixer_control *mc = (void *)kctl->private_value; ++ ++ mc->w = w; ++ module->kctl = kctl; ++ list_add_tail(&module->node, &ids->gain_list); ++ } else if (type == SST_MODULE_ALGO) { ++ struct sst_algo_control *bc = (void *)kctl->private_value; ++ ++ bc->w = w; ++ module->kctl = kctl; ++ list_add_tail(&module->node, &ids->algo_list); ++ } ++ ++ return 0; ++} ++ ++static int sst_fill_widget_module_info(struct snd_soc_dapm_widget *w, ++ struct snd_soc_platform *platform) ++{ ++ struct snd_kcontrol *kctl; ++ int index, ret = 0; ++ struct snd_card *card = platform->card->snd_card; ++ char *idx; ++ ++ down_read(&card->controls_rwsem); ++ ++ list_for_each_entry(kctl, &card->controls, list) { ++ idx = strstr(kctl->id.name, " "); ++ if (idx == NULL) ++ continue; ++ index = strlen(kctl->id.name) - strlen(idx); ++ if (strstr(kctl->id.name, "volume") && ++ !strncmp(kctl->id.name, w->name, index)) ++ ret = sst_fill_module_list(kctl, w, SST_MODULE_GAIN); ++ else if (strstr(kctl->id.name, "params") && ++ !strncmp(kctl->id.name, w->name, index)) ++ ret = sst_fill_module_list(kctl, w, SST_MODULE_ALGO); ++ else if (strstr(kctl->id.name, "mute") && ++ !strncmp(kctl->id.name, w->name, index)) { ++ struct sst_gain_mixer_control *mc = (void *)kctl->private_value; ++ mc->w = w; ++ } ++ if (ret < 0) { ++ up_read(&card->controls_rwsem); ++ return ret; ++ } ++ } ++ up_read(&card->controls_rwsem); ++ return 0; ++} ++ ++static int sst_map_modules_to_pipe(struct snd_soc_platform *platform) ++{ ++ struct snd_soc_dapm_widget *w; ++ struct snd_soc_dapm_context *dapm = &platform->dapm; ++ int ret = 0; ++ ++ list_for_each_entry(w, &dapm->card->widgets, list) { ++ if (w->platform && is_sst_dapm_widget(w) && (w->priv)) { ++ struct sst_ids *ids = w->priv; ++ ++ pr_debug("widget type=%d name=%s", w->id, w->name); ++ INIT_LIST_HEAD(&ids->algo_list); ++ INIT_LIST_HEAD(&ids->gain_list); ++ ret = sst_fill_widget_module_info(w, platform); ++ if (ret < 0) ++ return ret; ++ } ++ } ++ return 0; ++} ++ ++int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform) ++{ ++ int i, ret = 0; ++ struct sst_data *sst = snd_soc_platform_get_drvdata(platform); ++ ++ sst->byte_stream = devm_kzalloc(platform->dev, ++ SST_MAX_BIN_BYTES, GFP_KERNEL); ++ if (!sst->byte_stream) { ++ pr_err("%s: kzalloc failed\n", __func__); ++ return -ENOMEM; ++ } ++ sst->widget = devm_kzalloc(platform->dev, ++ SST_NUM_WIDGETS * sizeof(*sst->widget), ++ GFP_KERNEL); ++ if (!sst->widget) { ++ pr_err("%s: kzalloc failed\n", __func__); ++ return -ENOMEM; ++ } ++ ++ snd_soc_dapm_new_controls(&platform->dapm, sst_dapm_widgets, ++ ARRAY_SIZE(sst_dapm_widgets)); ++ snd_soc_dapm_add_routes(&platform->dapm, intercon, ++ ARRAY_SIZE(intercon)); ++ snd_soc_dapm_new_widgets(&platform->dapm); ++ ++ for (i = 0; i < SST_NUM_GAINS; i++) { ++ sst_gains[i].mute = SST_GAIN_MUTE_DEFAULT; ++ sst_gains[i].l_gain = SST_GAIN_VOLUME_DEFAULT; ++ sst_gains[i].r_gain = SST_GAIN_VOLUME_DEFAULT; ++ sst_gains[i].ramp_duration = SST_GAIN_RAMP_DURATION_DEFAULT; ++ } ++ ++ snd_soc_add_platform_controls(platform, sst_gain_controls, ++ ARRAY_SIZE(sst_gain_controls)); ++ ++ snd_soc_add_platform_controls(platform, sst_algo_controls, ++ ARRAY_SIZE(sst_algo_controls)); ++ snd_soc_add_platform_controls(platform, sst_slot_controls, ++ ARRAY_SIZE(sst_slot_controls)); ++ snd_soc_add_platform_controls(platform, sst_mux_controls, ++ ARRAY_SIZE(sst_mux_controls)); ++ ++ /* initialize the names of the probe points */ ++ for (i = 0; i < SST_NUM_PROBE_CONNECTION_PTS; i++) ++ sst_probe_enum_texts[i] = sst_probes[i].name; ++ ++ snd_soc_add_platform_controls(platform, sst_probe_controls, ++ ARRAY_SIZE(sst_probe_controls)); ++ ++ ret = sst_map_modules_to_pipe(platform); ++ ++ return ret; ++} +diff --git a/sound/soc/intel/platform-libs/ipc_lib.h b/sound/soc/intel/platform-libs/ipc_lib.h +new file mode 100644 +index 0000000..6ba50c4 +--- /dev/null ++++ b/sound/soc/intel/platform-libs/ipc_lib.h +@@ -0,0 +1,33 @@ ++/* ++ * ipc_lib.h - Intel MID Platform driver header file ++ * ++ * Copyright (C) 2013 Intel Corp ++ * Author: Lakshmi N Vinnakota ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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 __PLATFORMDRV_IPC_LIB_H__ ++#define __PLATFORMDRV_IPC_LIB_H__ ++ ++struct sst_algo_int_control_v2; ++ ++void sst_create_compr_vol_ipc(char *bytes, unsigned int type, ++ struct sst_algo_int_control_v2 *kdata); ++#endif +diff --git a/sound/soc/intel/platform-libs/ipc_lib_v2.c b/sound/soc/intel/platform-libs/ipc_lib_v2.c +new file mode 100644 +index 0000000..105c756 +--- /dev/null ++++ b/sound/soc/intel/platform-libs/ipc_lib_v2.c +@@ -0,0 +1,109 @@ ++/* ++ * ipc_lib_v2.c - Intel MID Platform Driver IPC wrappers for mrfld ++ * ++ * Copyright (C) 2013 Intel Corp ++ * Author: Lakshmi N Vinnakota ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * ++ */ ++#include ++#include ++#include "../platform_ipc_v2.h" ++#include "../sst_platform.h" ++#include "../sst_platform_pvt.h" ++ ++ ++static inline void sst_fill_dsp_hdr(struct ipc_dsp_hdr *hdr, u8 index, u8 pipe, ++ u16 module, u16 cmd, u16 len) ++{ ++ hdr->mod_index_id = index; ++ hdr->pipe_id = pipe; ++ hdr->mod_id = module; ++ hdr->cmd_id = cmd; ++ hdr->length = len; ++ ++} ++ ++static inline void sst_fill_byte_control_hdr(struct snd_sst_bytes_v2 *hdr, ++ u8 type, u8 msg, u8 block, u8 task, u8 pipe, u16 len) ++{ ++ hdr->type = type; ++ hdr->ipc_msg = msg; ++ hdr->block = block; ++ hdr->task_id = task; ++ hdr->pipe_id = pipe; ++ hdr->rsvd = 0; ++ hdr->len = len; ++} ++ ++#define SST_GAIN_V2_TIME_CONST 50 ++ ++void sst_create_compr_vol_ipc(char *bytes, unsigned int type, ++ struct sst_algo_int_control_v2 *kdata) ++{ ++ struct snd_sst_gain_v2 gain1; ++ struct snd_sst_bytes_v2 byte_hdr; ++ struct ipc_dsp_hdr dsp_hdr; ++ char *tmp; ++ u16 len; ++ u8 ipc_msg; ++ ++ /* Fill gain params */ ++ gain1.gain_cell_num = 1; /* num of gain cells to modify*/ ++ gain1.cell_nbr_idx = kdata->instance_id; /* instance index */ ++ gain1.cell_path_idx = kdata->pipe_id; /* pipe id */ ++ gain1.module_id = kdata->module_id; /*module id */ ++ gain1.left_cell_gain = kdata->value; /* left gain value in dB*/ ++ gain1.right_cell_gain = kdata->value; /* same value as left in dB*/ ++ /* set to default recommended value*/ ++ gain1.gain_time_const = SST_GAIN_V2_TIME_CONST; ++ ++ /* fill dsp header */ ++ /* Get params format for vol ctrl lib, size 6 bytes : ++ * u16 left_gain, u16 right_gain, u16 ramp ++ */ ++ memset(&dsp_hdr, 0, sizeof(dsp_hdr)); ++ if (type == SND_SST_BYTES_GET) { ++ len = 6; ++ ipc_msg = IPC_GET_PARAMS; ++ } else { ++ len = sizeof(gain1); ++ ipc_msg = IPC_SET_PARAMS; ++ } ++ ++ sst_fill_dsp_hdr(&dsp_hdr, 0, kdata->pipe_id, kdata->module_id, ++ IPC_IA_SET_GAIN_MRFLD, len); ++ ++ /* fill byte control header */ ++ memset(&byte_hdr, 0, sizeof(byte_hdr)); ++ len = sizeof(dsp_hdr) + dsp_hdr.length; ++ sst_fill_byte_control_hdr(&byte_hdr, type, ipc_msg, 1, ++ SST_TASK_ID_MEDIA, kdata->pipe_id, len); ++ ++ /* fill complete byte stream as ipc payload */ ++ tmp = bytes; ++ memcpy(tmp, &byte_hdr, sizeof(byte_hdr)); ++ memcpy((tmp + sizeof(byte_hdr)), &dsp_hdr, sizeof(dsp_hdr)); ++ if (type != SND_SST_BYTES_GET) ++ memcpy((tmp + sizeof(byte_hdr) + sizeof(dsp_hdr)), &gain1, ++ sizeof(gain1)); ++#ifdef DEBUG_HEX_DUMP_BYTES ++ print_hex_dump_bytes(__func__, DUMP_PREFIX_NONE, bytes, 32); ++#endif ++} +diff --git a/sound/soc/intel/platform-libs/sst_widgets.h b/sound/soc/intel/platform-libs/sst_widgets.h +new file mode 100644 +index 0000000..d39ec89 +--- /dev/null ++++ b/sound/soc/intel/platform-libs/sst_widgets.h +@@ -0,0 +1,337 @@ ++/* ++ * sst_widgets.h - Intel helpers to generate FW widgets ++ * ++ * Copyright (C) 2013 Intel Corp ++ * Author: Omair Mohammed Abdullah ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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_WIDGETS_H__ ++#define __SST_WIDGETS_H__ ++ ++#include ++#include ++ ++#define SST_MODULE_GAIN 1 ++#define SST_MODULE_ALGO 2 ++ ++#define SST_FMT_MONO 0 ++#define SST_FMT_STEREO 3 ++ ++/* physical SSP numbers */ ++enum { ++ SST_SSP0 = 0, ++ SST_SSP1, ++ SST_SSP2, ++ SST_SSP_LAST = SST_SSP2, ++}; ++ ++#define SST_NUM_SSPS (SST_SSP_LAST + 1) /* physical SSPs */ ++#define SST_MAX_SSP_MUX 2 /* single SSP muxed between pipes */ ++#define SST_MAX_SSP_DOMAINS 2 /* domains present in each pipe */ ++ ++struct module { ++ struct snd_kcontrol *kctl; ++ struct list_head node; ++}; ++ ++struct sst_ssp_config { ++ u8 ssp_id; ++ u8 bits_per_slot; ++ u8 slots; ++ u8 ssp_mode; ++ u8 pcm_mode; ++ u8 duplex; ++ u8 ssp_protocol; ++ u8 fs_frequency; ++ u8 active_slot_map; ++ u8 start_delay; ++ u16 fs_width; ++}; ++ ++struct sst_ssp_cfg { ++ const u8 ssp_number; ++ const int *mux_shift; ++ const int (*domain_shift)[SST_MAX_SSP_MUX]; ++ const struct sst_ssp_config (*ssp_config)[SST_MAX_SSP_MUX][SST_MAX_SSP_DOMAINS]; ++}; ++ ++struct sst_ids { ++ u16 location_id; ++ u16 module_id; ++ u8 task_id; ++ u8 format; ++ u8 reg; ++ struct list_head algo_list; ++ struct list_head gain_list; ++ const struct sst_ssp_cfg *ssp; ++}; ++ ++#define SST_SSP_AIF_IN(wname, wevent, wssp_cfg) \ ++{ .id = snd_soc_dapm_aif_in, .name = wname, .sname = NULL, \ ++ .reg = SND_SOC_NOPM, .shift = 0, .invert = 0, \ ++ .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ ++ .priv = (void *)&(struct sst_ids) { .ssp = &wssp_cfg, } \ ++} ++ ++#define SST_SSP_AIF_OUT(wname, wevent, wssp_cfg) \ ++{ .id = snd_soc_dapm_aif_out, .name = wname, .sname = NULL, \ ++ .reg = SND_SOC_NOPM, .shift = 0, .invert = 0, \ ++ .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ ++ .priv = (void *)&(struct sst_ids) { .ssp = &wssp_cfg, } \ ++} ++ ++#define SST_SSP_INPUT(wname, wevent, wssp_cfg) \ ++{ .id = snd_soc_dapm_input, .name = wname, .sname = NULL, \ ++ .reg = SND_SOC_NOPM, .shift = 0, .invert = 0, \ ++ .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ ++ .priv = (void *)&(struct sst_ids) { .ssp = &wssp_cfg, } \ ++} ++ ++#define SST_SSP_OUTPUT(wname, wevent, wssp_cfg) \ ++{ .id = snd_soc_dapm_output, .name = wname, .sname = NULL, \ ++ .reg = SND_SOC_NOPM, .shift = 0, .invert = 0, \ ++ .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ ++ .priv = (void *)&(struct sst_ids) { .ssp = &wssp_cfg, } \ ++} ++ ++#define SST_PATH(wname, wtask, wloc_id, wevent, wflags) \ ++{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \ ++ .invert = 0, .kcontrol_news = NULL, .num_kcontrols = 0, \ ++ .event = wevent, .event_flags = wflags, \ ++ .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, } \ ++} ++ ++#define SST_PATH_MEDIA_LOOP(wname, wtask, wloc_id, wformat, wevent, wflags) \ ++{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \ ++ .invert = 0, .kcontrol_news = NULL, .num_kcontrols = 0, \ ++ .event = wevent, .event_flags = wflags, \ ++ .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, \ ++ .format = wformat,} \ ++} ++ ++/* output is triggered before input */ ++#define SST_PATH_INPUT(name, task_id, loc_id, event) \ ++ SST_PATH(name, task_id, loc_id, event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD) ++ ++#define SST_PATH_OUTPUT(name, task_id, loc_id, event) \ ++ SST_PATH(name, task_id, loc_id, event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD) ++ ++#define SST_PATH_MEDIA_LOOP_OUTPUT(name, task_id, loc_id, format, event) \ ++ SST_PATH_MEDIA_LOOP(name, task_id, loc_id, format, event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD) ++ ++ ++#define SST_SWM_MIXER(wname, wreg, wtask, wloc_id, wcontrols, wevent) \ ++{ .id = snd_soc_dapm_mixer, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \ ++ .invert = 0, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols),\ ++ .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD | \ ++ SND_SOC_DAPM_POST_REG, \ ++ .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, \ ++ .reg = wreg } \ ++} ++ ++enum sst_gain_kcontrol_type { ++ SST_GAIN_TLV, ++ SST_GAIN_MUTE, ++ SST_GAIN_RAMP_DURATION, ++}; ++ ++struct sst_gain_mixer_control { ++ bool stereo; ++ enum sst_gain_kcontrol_type type; ++ struct sst_gain_value *gain_val; ++ int max; ++ int min; ++ u16 instance_id; ++ u16 module_id; ++ u16 pipe_id; ++ u16 task_id; ++ char pname[44]; ++ struct snd_soc_dapm_widget *w; ++}; ++ ++struct sst_gain_value { ++ u16 ramp_duration; ++ s16 l_gain; ++ s16 r_gain; ++ bool mute; ++}; ++ ++#define SST_GAIN_VOLUME_DEFAULT (-1440) ++#define SST_GAIN_RAMP_DURATION_DEFAULT 5 /* timeconstant */ ++#define SST_GAIN_MUTE_DEFAULT true ++ ++#define SST_GAIN_KCONTROL_TLV(xname, xhandler_get, xhandler_put, \ ++ xmod, xpipe, xinstance, xtask, tlv_array, xgain_val, \ ++ xmin, xmax, xpname) \ ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ ++ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ ++ SNDRV_CTL_ELEM_ACCESS_READWRITE, \ ++ .tlv.p = (tlv_array), \ ++ .info = sst_gain_ctl_info,\ ++ .get = xhandler_get, .put = xhandler_put, \ ++ .private_value = (unsigned long)&(struct sst_gain_mixer_control) \ ++ { .stereo = true, .max = xmax, .min = xmin, .type = SST_GAIN_TLV, \ ++ .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\ ++ .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname} ++ ++#define SST_GAIN_KCONTROL_INT(xname, xhandler_get, xhandler_put, \ ++ xmod, xpipe, xinstance, xtask, xtype, xgain_val, \ ++ xmin, xmax, xpname) \ ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ ++ .info = sst_gain_ctl_info, \ ++ .get = xhandler_get, .put = xhandler_put, \ ++ .private_value = (unsigned long)&(struct sst_gain_mixer_control) \ ++ { .stereo = false, .max = xmax, .min = xmin, .type = xtype, \ ++ .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\ ++ .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname} ++ ++#define SST_GAIN_KCONTROL_BOOL(xname, xhandler_get, xhandler_put,\ ++ xmod, xpipe, xinstance, xtask, xgain_val, xpname) \ ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ ++ .info = snd_soc_info_bool_ext, \ ++ .get = xhandler_get, .put = xhandler_put, \ ++ .private_value = (unsigned long)&(struct sst_gain_mixer_control) \ ++ { .stereo = false, .type = SST_GAIN_MUTE, \ ++ .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\ ++ .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname} ++ ++#define SST_CONTROL_NAME(xpname, xmname, xinstance, xtype) \ ++ xpname " " xmname " " #xinstance " " xtype ++ ++#define SST_COMBO_CONTROL_NAME(xpname, xmname, xinstance, xtype, xsubmodule) \ ++ xpname " " xmname " " #xinstance " " xtype " " xsubmodule ++ ++/* ++ * 3 Controls for each Gain module ++ * e.g. - pcm0_in gain 0 volume ++ * - pcm0_in gain 0 rampduration ++ * - pcm0_in gain 0 mute ++ */ ++#define SST_GAIN_KCONTROLS(xpname, xmname, xmin_gain, xmax_gain, xmin_tc, xmax_tc, \ ++ xhandler_get, xhandler_put, \ ++ xmod, xpipe, xinstance, xtask, tlv_array, xgain_val) \ ++ { SST_GAIN_KCONTROL_INT(SST_CONTROL_NAME(xpname, xmname, xinstance, "rampduration"), \ ++ xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, SST_GAIN_RAMP_DURATION, \ ++ xgain_val, xmin_tc, xmax_tc, xpname) }, \ ++ { SST_GAIN_KCONTROL_BOOL(SST_CONTROL_NAME(xpname, xmname, xinstance, "mute"), \ ++ xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, \ ++ xgain_val, xpname) } ,\ ++ { SST_GAIN_KCONTROL_TLV(SST_CONTROL_NAME(xpname, xmname, xinstance, "volume"), \ ++ xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, tlv_array, \ ++ xgain_val, xmin_gain, xmax_gain, xpname) } ++ ++#define SST_GAIN_TC_MIN 5 ++#define SST_GAIN_TC_MAX 5000 ++#define SST_GAIN_MIN_VALUE -1440 /* in 0.1 DB units */ ++#define SST_GAIN_MAX_VALUE 360 ++ ++enum sst_algo_kcontrol_type { ++ SST_ALGO_PARAMS, ++ SST_ALGO_BYPASS, ++}; ++ ++struct sst_algo_control { ++ enum sst_algo_kcontrol_type type; ++ int max; ++ u16 module_id; ++ u16 pipe_id; ++ u16 instance_id; ++ u16 task_id; ++ u16 cmd_id; ++ bool bypass; ++ unsigned char *params; ++ char pname[44]; ++ struct snd_soc_dapm_widget *w; ++}; ++ ++#define SST_ALGO_KCONTROL_BYTES(xpname, xmname, xcount, xmod, \ ++ xpipe, xinstance, xtask, xcmd) \ ++{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,\ ++ .name = SST_CONTROL_NAME(xpname, xmname, xinstance, "params"), \ ++ .info = sst_algo_bytes_ctl_info, \ ++ .get = sst_algo_control_get, .put = sst_algo_control_set, \ ++ .private_value = (unsigned long)&(struct sst_algo_control) \ ++ {.max = xcount, .type = SST_ALGO_PARAMS, .module_id = xmod, .pname = xpname, \ ++ .pipe_id = xpipe, .instance_id = xinstance, .task_id = xtask, .cmd_id = xcmd} } ++ ++#define SST_ALGO_KCONTROL_BOOL(xpname, xmname, xmod, xpipe, xinstance, xtask) \ ++{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ ++ .name = SST_CONTROL_NAME(xpname, xmname, xinstance, "bypass"), \ ++ .info = snd_soc_info_bool_ext, \ ++ .get = sst_algo_control_get, .put = sst_algo_control_set, \ ++ .private_value = (unsigned long)&(struct sst_algo_control) \ ++ {.type = SST_ALGO_BYPASS, .module_id = xmod, .pipe_id = xpipe, .pname = xpname, \ ++ .task_id = xtask, .instance_id = xinstance, .bypass = 0 } } ++ ++#define SST_ALGO_BYPASS_PARAMS(xpname, xmname, xcount, xmod, xpipe, \ ++ xinstance, xtask, xcmd) \ ++ SST_ALGO_KCONTROL_BOOL(xpname, xmname, xmod, xpipe, xinstance, xtask), \ ++ SST_ALGO_KCONTROL_BYTES(xpname, xmname, xcount, xmod, xpipe, xinstance, xtask, xcmd) ++ ++#define SST_COMBO_ALGO_KCONTROL_BYTES(xpname, xmname, xsubmod, xcount, xmod, \ ++ xpipe, xinstance, xtask, xcmd) \ ++{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,\ ++ .name = SST_COMBO_CONTROL_NAME(xpname, xmname, xinstance, "params", xsubmod), \ ++ .info = sst_algo_bytes_ctl_info, \ ++ .get = sst_algo_control_get, .put = sst_algo_control_set, \ ++ .private_value = (unsigned long)&(struct sst_algo_control) \ ++ {.max = xcount, .type = SST_ALGO_PARAMS, .module_id = xmod, .pname = xpname, \ ++ .pipe_id = xpipe, .instance_id = xinstance, .task_id = xtask, .cmd_id = xcmd} } ++ ++ ++/* only 4 slots/channels supported atm */ ++#define SST_SSP_SLOT_ENUM(s_ch_no, is_tx, xtexts) \ ++ (struct soc_enum){ .reg = s_ch_no, .reg2 = is_tx, .max = 4+1, .texts = xtexts, } ++ ++#define SST_SLOT_CTL_NAME(xpname, xmname, s_ch_name) \ ++ xpname " " xmname " " s_ch_name ++ ++#define SST_SSP_SLOT_CTL(xpname, xmname, s_ch_name, s_ch_no, is_tx, xtexts, xget, xput) \ ++ SOC_DAPM_ENUM_EXT(SST_SLOT_CTL_NAME(xpname, xmname, s_ch_name), \ ++ SST_SSP_SLOT_ENUM(s_ch_no, is_tx, xtexts), \ ++ xget, xput) ++ ++#define SST_MUX_CTL_NAME(xpname, xinstance) \ ++ xpname " " #xinstance ++ ++#define SST_SSP_MUX_ENUM(xreg, xshift, xtexts) \ ++ (struct soc_enum){ .reg = xreg, .texts = xtexts, .shift_l = xshift, \ ++ .shift_r = xshift, .max = ARRAY_SIZE(xtexts), } ++ ++#define SST_SSP_MUX_CTL(xpname, xinstance, xreg, xshift, xtexts, xget, xput) \ ++ SOC_DAPM_ENUM_EXT(SST_MUX_CTL_NAME(xpname, xinstance), \ ++ SST_SSP_MUX_ENUM(xreg, xshift, xtexts), \ ++ xget, xput) ++ ++struct sst_probe_value { ++ unsigned int val; ++ const struct soc_enum *p_enum; ++}; ++ ++#define SST_PROBE_CTL_NAME(dir, num, type) \ ++ dir #num " " type ++ ++#define SST_PROBE_ENUM(xname, xenum, xhandler_get, xhandler_put) \ ++{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ ++ .info = sst_probe_enum_info, \ ++ .get = xhandler_get, .put = xhandler_put, \ ++ .private_value = (unsigned long)&(struct sst_probe_value) \ ++ { .val = 0, .p_enum = &xenum } } ++ ++#endif +diff --git a/sound/soc/intel/platform_ipc_v2.h b/sound/soc/intel/platform_ipc_v2.h +new file mode 100644 +index 0000000..a5db16b +--- /dev/null ++++ b/sound/soc/intel/platform_ipc_v2.h +@@ -0,0 +1,693 @@ ++/* ++* platform_ipc_v2.h - Intel MID Platform driver FW IPC definitions ++* ++* Copyright (C) 2008-10 Intel Corporation ++* Author: Vinod Koul ++* Harsha Priya ++* Dharageswari R ++* KP Jeeja ++* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++* ++* 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. ++* ++* 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. ++* ++* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++* ++* This driver exposes the audio engine functionalities to the ALSA ++* and middleware. ++* This file has definitions shared between the firmware and driver ++*/ ++#ifndef __PLATFORM_IPC_V2_H__ ++#define __PLATFORM_IPC_V2_H__ ++ ++#define MAX_DBG_RW_BYTES 80 ++#define MAX_NUM_SCATTER_BUFFERS 8 ++#define MAX_LOOP_BACK_DWORDS 8 ++/* IPC base address and mailbox, timestamp offsets */ ++#define SST_MAILBOX_SIZE 0x0400 ++#define SST_MAILBOX_SEND 0x0000 ++#define SST_TIME_STAMP 0x1800 ++#define SST_TIME_STAMP_MRFLD 0x680 ++#define SST_TIME_STAMP_BYT 0x800 ++#define SST_RESERVED_OFFSET 0x1A00 ++#define SST_SCU_LPE_MAILBOX 0x1000 ++#define SST_LPE_SCU_MAILBOX 0x1400 ++#define SST_SCU_LPE_LOG_BUF (SST_SCU_LPE_MAILBOX+16) ++#define PROCESS_MSG 0x80 ++ ++/* Message ID's for IPC messages */ ++/* Bits B7: SST or IA/SC ; B6-B4: Msg Category; B3-B0: Msg Type */ ++ ++/* I2L Firmware/Codec Download msgs */ ++#define IPC_IA_PREP_LIB_DNLD 0x01 ++#define IPC_IA_LIB_DNLD_CMPLT 0x02 ++#define IPC_IA_GET_FW_VERSION 0x04 ++#define IPC_IA_GET_FW_BUILD_INF 0x05 ++#define IPC_IA_GET_FW_INFO 0x06 ++#define IPC_IA_GET_FW_CTXT 0x07 ++#define IPC_IA_SET_FW_CTXT 0x08 ++#define IPC_IA_PREPARE_SHUTDOWN 0x31 ++/* I2L Codec Config/control msgs */ ++#define IPC_PREP_D3 0x10 ++#define IPC_IA_SET_CODEC_PARAMS 0x10 ++#define IPC_IA_GET_CODEC_PARAMS 0x11 ++#define IPC_IA_SET_PPP_PARAMS 0x12 ++#define IPC_IA_GET_PPP_PARAMS 0x13 ++#define IPC_SST_PERIOD_ELAPSED_MRFLD 0xA ++#define IPC_IA_ALG_PARAMS 0x1A ++#define IPC_IA_TUNING_PARAMS 0x1B ++#define IPC_IA_SET_RUNTIME_PARAMS 0x1C ++#define IPC_IA_SET_PARAMS 0x1 ++#define IPC_IA_GET_PARAMS 0x2 ++ ++#define IPC_EFFECTS_CREATE 0xE ++#define IPC_EFFECTS_DESTROY 0xF ++ ++/* I2L Stream config/control msgs */ ++#define IPC_IA_ALLOC_STREAM_MRFLD 0x2 ++#define IPC_IA_ALLOC_STREAM 0x20 /* Allocate a stream ID */ ++#define IPC_IA_FREE_STREAM_MRFLD 0x03 ++#define IPC_IA_FREE_STREAM 0x21 /* Free the stream ID */ ++#define IPC_IA_SET_STREAM_PARAMS 0x22 ++#define IPC_IA_SET_STREAM_PARAMS_MRFLD 0x12 ++#define IPC_IA_GET_STREAM_PARAMS 0x23 ++#define IPC_IA_PAUSE_STREAM 0x24 ++#define IPC_IA_PAUSE_STREAM_MRFLD 0x4 ++#define IPC_IA_RESUME_STREAM 0x25 ++#define IPC_IA_RESUME_STREAM_MRFLD 0x5 ++#define IPC_IA_DROP_STREAM 0x26 ++#define IPC_IA_DROP_STREAM_MRFLD 0x07 ++#define IPC_IA_DRAIN_STREAM 0x27 /* Short msg with str_id */ ++#define IPC_IA_DRAIN_STREAM_MRFLD 0x8 ++#define IPC_IA_CONTROL_ROUTING 0x29 ++#define IPC_IA_VTSV_UPDATE_MODULES 0x20 ++#define IPC_IA_VTSV_DETECTED 0x21 ++ ++#define IPC_IA_START_STREAM_MRFLD 0X06 ++#define IPC_IA_START_STREAM 0x30 /* Short msg with str_id */ ++ ++#define IPC_IA_SET_GAIN_MRFLD 0x21 ++/* Debug msgs */ ++#define IPC_IA_DBG_MEM_READ 0x40 ++#define IPC_IA_DBG_MEM_WRITE 0x41 ++#define IPC_IA_DBG_LOOP_BACK 0x42 ++#define IPC_IA_DBG_LOG_ENABLE 0x45 ++#define IPC_IA_DBG_SET_PROBE_PARAMS 0x47 ++ ++/* L2I Firmware/Codec Download msgs */ ++#define IPC_IA_FW_INIT_CMPLT 0x81 ++#define IPC_IA_FW_INIT_CMPLT_MRFLD 0x01 ++#define IPC_IA_FW_ASYNC_ERR_MRFLD 0x11 ++ ++/* L2I Codec Config/control msgs */ ++#define IPC_SST_FRAGMENT_ELPASED 0x90 /* Request IA more data */ ++ ++#define IPC_SST_BUF_UNDER_RUN 0x92 /* PB Under run and stopped */ ++#define IPC_SST_BUF_OVER_RUN 0x93 /* CAP Under run and stopped */ ++#define IPC_SST_DRAIN_END 0x94 /* PB Drain complete and stopped */ ++#define IPC_SST_CHNGE_SSP_PARAMS 0x95 /* PB SSP parameters changed */ ++#define IPC_SST_STREAM_PROCESS_FATAL_ERR 0x96/* error in processing a stream */ ++#define IPC_SST_PERIOD_ELAPSED 0x97 /* period elapsed */ ++ ++#define IPC_SST_ERROR_EVENT 0x99 /* Buffer over run occurred */ ++/* L2S messages */ ++#define IPC_SC_DDR_LINK_UP 0xC0 ++#define IPC_SC_DDR_LINK_DOWN 0xC1 ++#define IPC_SC_SET_LPECLK_REQ 0xC2 ++#define IPC_SC_SSP_BIT_BANG 0xC3 ++ ++/* L2I Error reporting msgs */ ++#define IPC_IA_MEM_ALLOC_FAIL 0xE0 ++#define IPC_IA_PROC_ERR 0xE1 /* error in processing a ++ stream can be used by playback and ++ capture modules */ ++ ++/* L2I Debug msgs */ ++#define IPC_IA_PRINT_STRING 0xF0 ++ ++/* Buffer under-run */ ++#define IPC_IA_BUF_UNDER_RUN_MRFLD 0x0B ++ ++/* Mrfld specific defines: ++ * For asynchronous messages(INIT_CMPLT, PERIOD_ELAPSED, ASYNC_ERROR) ++ * received from FW, the format is: ++ * - IPC High: pvt_id is set to zero. Always short message. ++ * - msg_id is in lower 16-bits of IPC low payload. ++ * - pipe_id is in higher 16-bits of IPC low payload for period_elapsed. ++ * - error id is in higher 16-bits of IPC low payload for async errors. ++ */ ++#define SST_ASYNC_DRV_ID 0 ++ ++/* Command Response or Acknowledge message to any IPC message will have ++ * same message ID and stream ID information which is sent. ++ * There is no specific Ack message ID. The data field is used as response ++ * meaning. ++ */ ++ ++/* SCU IPC for resetting & power gating the LPE through SCU */ ++#define IPC_SCU_LPE_RESET 0xA3 ++ ++enum ackData { ++ IPC_ACK_SUCCESS = 0, ++ IPC_ACK_FAILURE, ++}; ++ ++enum ipc_ia_msg_id { ++ IPC_CMD = 1, /*!< Task Control message ID */ ++ IPC_SET_PARAMS = 2,/*!< Task Set param message ID */ ++ IPC_GET_PARAMS = 3, /*!< Task Get param message ID */ ++ IPC_INVALID = 0xFF, /*! ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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 FORMAT(fmt) "%s: " fmt, __func__ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt) ++ ++#include ++#include "mid_ssp.h" ++ ++ ++/* ++ * Default I2S configuration ++ */ ++/* ++ * TO BE DONE: use mixer to make it more flexible ++ */ ++const struct intel_mid_i2s_settings ssp_platform_i2s_config = { ++ .master_mode_clk_selection = SSP_MASTER_CLOCK_UNDEFINED, ++ .master_mode_standard_freq = 0xFFFF, ++ .tx_tristate_phase = TXD_TRISTATE_LAST_PHASE_OFF, ++ .slave_clk_free_running_status = ++ SLAVE_SSPCLK_ON_DURING_TRANSFER_ONLY, ++ .ssp_duplex_mode = RX_AND_TX_MODE, ++ .ssp_trailing_byte_mode = SSP_TRAILING_BYTE_HDL_BY_IA, ++ .ssp_tx_dma = SSP_TX_DMA_ENABLE, ++ .ssp_rx_dma = SSP_RX_DMA_ENABLE, ++ .rx_fifo_interrupt = SSP_RX_FIFO_OVER_INT_ENABLE, ++ .tx_fifo_interrupt = SSP_TX_FIFO_UNDER_INT_ENABLE, ++ .ssp_rx_timeout_interrupt_status = SSP_RX_TIMEOUT_INT_DISABLE, ++ .ssp_trailing_byte_interrupt_status = ++ SSP_TRAILING_BYTE_INT_ENABLE, ++ .ssp_loopback_mode_status = SSP_LOOPBACK_OFF, ++ .ssp_rx_fifo_threshold = MID_SSP_RX_FIFO_THRESHOLD, ++ .ssp_tx_fifo_threshold = MID_SSP_TX_FIFO_THRESHOLD, ++ .ssp_frmsync_pol_bit = SSP_FRMS_ACTIVE_HIGH, ++ .ssp_end_transfer_state = ++ SSP_END_DATA_TRANSFER_STATE_LOW, ++ .ssp_psp_T1 = 0, ++ .ssp_psp_T2 = 0, ++ .ssp_psp_T4 = 0, ++ .ssp_psp_T5 = 0, ++ .ssp_psp_T6 = 1, ++}; ++ ++/* ++ * SSP DAI Internal functions ++ */ ++ ++/** ++ * ssp_dma_req - This function programs a write or read request ++ * to the Intel I2S driver ++ * ++ * @param substream Pointer to stream structure ++ * return ret_val Status ++ */ ++static int ssp_dma_req(struct snd_pcm_substream *substream) ++{ ++ ++ struct intel_alsa_ssp_stream_info *str_info; ++ struct intel_ssp_config *ssp_config; ++ struct snd_pcm_runtime *pl_runtime; ++ int ret; ++#ifdef _LLI_ENABLED_ ++ struct intel_mid_i2s_lli *sg_table = NULL; ++ int i; ++#else ++ u32 *dma_addr; ++#endif /* _LLI_ENABLED_ */ ++ ++ WARN(!substream, "SSP DAI: " ++ "ERROR NULL substream\n"); ++ if (!substream) ++ return -EINVAL; ++ ++ ++ pl_runtime = substream->runtime; ++ ++ str_info = pl_runtime->private_data; ++ ++ WARN(!str_info, "SSP DAI: " ++ "ERROR NULL str_info\n"); ++ if (!str_info) ++ return -EINVAL; ++ ++ ssp_config = str_info->ssp_config; ++ ++ WARN(!ssp_config, "SSP DAI: " ++ "ERROR NULL ssp_config\n"); ++ if (!ssp_config) ++ return -EINVAL; ++ ++ ++ WARN(!ssp_config->i2s_handle, "SSP DAI: " ++ "ERROR, trying to play a stream however " ++ "ssp_config->i2s_handle is NULL\n"); ++ ++ if (!ssp_config->i2s_handle) ++ return -EINVAL; ++ ++#ifdef _LLI_ENABLED_ ++ if (!test_bit(INTEL_ALSA_SSP_STREAM_STARTED, ++ &str_info->stream_status)) { ++ pr_err("%s: Stream has been stopped before SSP DMA request has been taken into account", ++ __func__); ++ return 0; ++ } ++ ++ /* Will be executed once until next DAI shutdown */ ++ if (!test_bit(INTEL_ALSA_SSP_STREAM_INIT, ++ &str_info->stream_status)) { ++ ++ str_info->length = frames_to_bytes(pl_runtime, ++ pl_runtime->period_size); ++ ++ str_info->addr = substream->runtime->dma_area; ++ ++ sg_table = kzalloc(sizeof(struct intel_mid_i2s_lli) * ++ pl_runtime->periods, ++ GFP_KERNEL); ++ if (sg_table == NULL) { ++ pr_err("sg_table allocation failed!"); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < pl_runtime->periods; i++) { ++ sg_table[i].addr = (u32 *) (str_info->addr + ++ str_info->length * i); ++ sg_table[i].leng = (u32) str_info->length; ++ } ++ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ++ ret = intel_mid_i2s_lli_wr_req(ssp_config->i2s_handle, ++ sg_table, ++ pl_runtime->periods, ++ I2S_CIRCULAR_MODE, ++ substream); ++ else ++ ret = intel_mid_i2s_lli_rd_req(ssp_config->i2s_handle, ++ sg_table, ++ pl_runtime->periods, ++ I2S_CIRCULAR_MODE, ++ substream); ++ kfree(sg_table); ++ ++ if (ret != 0) { ++ pr_err("SSP DAI: %s request error", ++ (substream->stream == ++ SNDRV_PCM_STREAM_PLAYBACK) ? ++ "write" : "read"); ++ } ++ ++ set_bit(INTEL_ALSA_SSP_STREAM_INIT, &str_info->stream_status); ++ ++ intel_mid_i2s_command(ssp_config->i2s_handle, ++ SSP_CMD_ENABLE_SSP, NULL); ++ } ++ ++ /* Executed at each TRIGGER_START */ ++ intel_mid_i2s_command(ssp_config->i2s_handle, ++ (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? ++ SSP_CMD_ENABLE_DMA_TX_INTR : ++ SSP_CMD_ENABLE_DMA_RX_INTR, NULL); ++ ++ return 0; ++#else ++ str_info->length = frames_to_bytes(pl_runtime, pl_runtime->period_size); ++ ++ str_info->addr = substream->runtime->dma_area; ++ pr_debug("SSP DAI: FCT %s substream->runtime->dma_area = %p", ++ __func__, substream->runtime->dma_area); ++ ++ dma_addr = (u32 *)(str_info->addr + str_info->length ++ * str_info->period_req_index); ++ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ++ ret = intel_mid_i2s_wr_req(ssp_config->i2s_handle, dma_addr, ++ str_info->length, substream); ++ else ++ ret = intel_mid_i2s_rd_req(ssp_config->i2s_handle, dma_addr, ++ str_info->length, substream); ++ ++ if (ret == 0) { ++ intel_mid_i2s_command(ssp_config->i2s_handle, ++ SSP_CMD_ENABLE_SSP, NULL); ++ ++ if (test_and_set_bit(INTEL_ALSA_SSP_STREAM_RUNNING, ++ &str_info->stream_status)) { ++ pr_err("SSP DAI: ERROR previous request not handled\n"); ++ return -EBUSY; ++ } ++ ++ if (++(str_info->period_req_index) >= pl_runtime->periods) ++ str_info->period_req_index = 0; ++ return 0; ++ } else { ++ pr_err("SSP DAI: FCT %s read/write req ERROR\n", __func__); ++ return -EINVAL; ++ } ++#endif /* _LLI_ENABLED_ */ ++} /* ssp_dma_req */ ++ ++/** ++ * ssp_dma_complete - End of capture or playback callback ++ * called in DMA Complete Tasklet context ++ * This Callback has in charge of re-programming a new read or write ++ * request to Intel MID I2S Driver if the stream has not been Closed. ++ * It calls also the snd_pcm_period_elapsed if the stream is not ++ * PAUSED or SUSPENDED to inform ALSA Kernel that the Ring Buffer ++ * period has been sent or received properly ++ * ++ * @param param Pointer to a user data ++ * return status ++ */ ++static int ssp_dma_complete(void *param) ++{ ++ struct snd_pcm_substream *substream; ++ struct intel_alsa_ssp_stream_info *str_info; ++ struct snd_pcm_runtime *pl_runtime; ++#ifndef _LLI_ENABLED_ ++ bool call_back = false; ++ bool reset_index = false; ++#endif /* _LLI_ENABLED_ */ ++ ++ substream = (struct snd_pcm_substream *)param; ++ pl_runtime = substream->runtime; ++ str_info = substream->runtime->private_data; ++ ++ WARN(!str_info, "SSP DAI: ERROR NULL str_info\n"); ++ if (str_info == NULL) ++ return -EINVAL; ++ ++#ifdef _LLI_ENABLED_ ++ if (!test_bit(INTEL_ALSA_SSP_STREAM_INIT, ++ &str_info->stream_status)) { ++ pr_err("Stream already not initialized"); ++ return 0; ++ } ++ ++ if (++(str_info->period_cb_index) >= pl_runtime->periods) ++ str_info->period_cb_index = 0; ++ ++ if (test_bit(INTEL_ALSA_SSP_STREAM_STARTED, &str_info->stream_status)) ++ snd_pcm_period_elapsed(substream); ++ else ++ pr_debug("No call to snd_period_elapsed, stream is not started"); ++#else ++ if (test_and_clear_bit(INTEL_ALSA_SSP_STREAM_RUNNING, ++ &str_info->stream_status)) { ++ bool dropped = test_and_clear_bit(INTEL_ALSA_SSP_STREAM_DROPPED, ++ &str_info->stream_status); ++ bool started = test_bit(INTEL_ALSA_SSP_STREAM_STARTED, ++ &str_info->stream_status); ++ ++ if (started) { ++ /* ++ * Whatever dropped or not, ++ * the stream is on going ++ */ ++ call_back = true; ++ } ++ if (started && dropped) { ++ /* ++ * the stream has been dropped and restarted ++ * before the callback occurs ++ * in this case the we have to reprogram the ++ * requests to SSP driver ++ * and reset the stream's indexes ++ */ ++ reset_index = true; ++ } ++ if (!started && !dropped) { ++ pr_err("SSP DAI: FCT %s neither started nor dropped", ++ __func__); ++ return -EBUSY; ++ } ++ } else { ++ pr_err("SSP DAI: FCT %s called while not running ", __func__); ++ return -EBUSY; ++ } ++ ++ if (call_back == true) { ++ pr_debug("SSP DAI: playback/capture (REQ=%d,CB=%d): DMA_REQ_COMPLETE\n", ++ str_info->period_req_index, ++ str_info->period_cb_index); ++ ++ if (reset_index) { ++ str_info->period_cb_index = 0; ++ str_info->period_req_index = 0; ++ } else if (++(str_info->period_cb_index) >= pl_runtime->periods) ++ str_info->period_cb_index = 0; ++ ++ /* ++ * Launch the next Capture/Playback request if ++ * no CLOSE has been requested ++ */ ++ ssp_dma_req(substream); ++ ++ /* ++ * Call the snd_pcm_period_elapsed to inform ALSA kernel ++ * that a ringbuffer period has been played ++ */ ++ snd_pcm_period_elapsed(substream); ++ } ++#endif /* _LLI_ENABLED_ */ ++ ++ return 0; ++} /* ssp_dma_complete */ ++ ++/** ++ * intel_mid_ssp_transfer_data - send data buffers ++ * ++ * @param work Pointer to stream structure ++ * return void ++ */ ++void intel_mid_ssp_transfer_data(struct work_struct *work) ++{ ++ struct intel_alsa_ssp_stream_info *str_info; ++ struct snd_pcm_substream *substream; ++ ++ BUG_ON(!work); ++ ++ str_info = container_of(work, struct intel_alsa_ssp_stream_info, ++ ssp_ws); ++ ++ BUG_ON(!str_info); ++ ++ substream = str_info->substream; ++ ++ BUG_ON(!substream); ++ ++ ssp_dma_req(substream); ++ ++} /* intel_mid_ssp_transfer_data */ ++ ++/* ++ * SSP PLATFORM ++ */ ++ ++/* ++ * SSP Platform functions ++ */ ++static int ssp_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime) ++{ ++ int retval = 0; ++ struct snd_soc_dai *dai; ++ struct snd_pcm *pcm; ++ ++ pr_debug("SSP DAI: FCT %s enters\n", ++ __func__); ++ /* ++ * Do pre-allocation to all substreams of the given pcm for the ++ * specified DMA type. ++ * ++ */ ++ dai = soc_runtime->cpu_dai; ++ pcm = soc_runtime->pcm; ++ ++ if (dai->driver->playback.channels_min || ++ dai->driver->capture.channels_min) { ++ retval = snd_pcm_lib_preallocate_pages_for_all(pcm, ++ SNDRV_DMA_TYPE_CONTINUOUS, ++ snd_dma_continuous_data(GFP_KERNEL), ++ SSP_MIN_BUFFER, SSP_MAX_BUFFER); ++ ++ if (retval) { ++ pr_err("DMA buffer allocation fail\n"); ++ return retval; ++ } ++ } ++ return retval; ++} /* ssp_platform_pcm_new */ ++ ++static void ssp_platform_pcm_free(struct snd_pcm *pcm) ++{ ++ pr_debug("SSP DAI: FCT %s enter\n", ++ __func__); ++ /* ++ * release all pre-allocated buffers on the pcm ++ * ++ */ ++ snd_pcm_lib_preallocate_free_for_all(pcm); ++ ++} /* ssp_platform_pcm_free */ ++ ++/** ++ * ssp_platform_hw_params - Allocate memory for Ring Buffer according ++ * to hw_params. ++ * It's called in a non-atomic context ++ * ++ * @param substream Substream for which the stream function is called ++ * @param hw_params Stream command thats requested from upper layer ++ * return status 0 ==> OK ++ * ++ */ ++static int ssp_platform_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *hw_params) ++{ ++ int ret_val; ++ ++ /* ++ * Allocates the DMA buffer for the substream ++ * This callback could be called several time ++ * snd_pcm_lib_malloc_pages allows to avoid memory leak ++ * as it release already allocated memory when already allocated ++ */ ++ ret_val = snd_pcm_lib_malloc_pages(substream, ++ params_buffer_bytes(hw_params)); ++ ++ if (ret_val < 0) ++ return ret_val; ++ ++ memset(substream->runtime->dma_area, 0, params_buffer_bytes(hw_params)); ++ ++ return 0; ++} /* ssp_platform_hw_params */ ++ ++/* ++ * ssp_platform_pointer- to send the current buffer pointer ++ * processed by HW ++ * This function is called by ALSA framework to get the current HW buffer ptr ++ * to check the Ring Buffer Status ++ * ++ * @param substream Pointer to the substream for which the function ++ * is called ++ * ++ * return pcm_pointer Indicates the number of samples played ++ * ++ */ ++static ++snd_pcm_uframes_t ssp_platform_pointer(struct snd_pcm_substream *substream) ++{ ++ struct intel_alsa_ssp_stream_info *str_info; ++ unsigned long pcm_pointer = 0; ++ ++ str_info = substream->runtime->private_data; ++ ++ WARN(!str_info, "SSP DAI: ERROR NULL str_info\n"); ++ if (!str_info) ++ return -EINVAL; ++ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ++ pcm_pointer = (unsigned long) (str_info->period_cb_index ++ * substream->runtime->period_size); ++ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ++ pcm_pointer = (unsigned long) (str_info->period_cb_index ++ * substream->runtime->period_size); ++ ++ pr_debug("SSP DAI: FCT %s Frame bits = %d, period_size = %d, periods = %d\n", ++ __func__, ++ (int) substream->runtime->frame_bits, ++ (int) substream->runtime->period_size, ++ (int) substream->runtime->periods); ++ ++ pr_debug("SSP DAI: FCT %s returns %ld\n", ++ __func__, pcm_pointer); ++ ++ return pcm_pointer; ++} /* ssp_platform_pointer */ ++ ++static struct snd_pcm_ops ssp_platform_ops = { ++ .open = NULL, ++ .close = NULL, ++ .ioctl = snd_pcm_lib_ioctl, ++ .hw_params = ssp_platform_hw_params, ++ .hw_free = NULL, ++ .prepare = NULL, ++ .trigger = NULL, ++ .pointer = ssp_platform_pointer, ++}; ++ ++struct snd_soc_platform_driver soc_ssp_platform_drv = { ++ .ops = &ssp_platform_ops, ++ .probe = NULL, ++ .pcm_new = ssp_platform_pcm_new, ++ .pcm_free = ssp_platform_pcm_free, ++}; ++ ++/* ++ * SND SOC DAI OPs ++ */ ++static int ssp_probe(struct snd_soc_dai *cpu_dai) ++{ ++ struct intel_ssp_config *ssp_config; ++ ++ pr_info("SSP DAI: FCT %s enters for CPU_DAI %d\n", ++ __func__, cpu_dai->id); ++ ++ ssp_config = kzalloc(sizeof(struct intel_ssp_config), GFP_KERNEL); ++ ++ ++ if (ssp_config == NULL) { ++ pr_err("Unable to allocate ssp_config\n"); ++ return -ENOMEM; ++ } ++ ++#ifndef _LLI_ENABLED_ ++ ssp_config->intel_mid_dma_alloc = false; ++#endif /* _LLI_ENABLED_ */ ++ ssp_config->ssp_dai_tx_allocated = false; ++ ssp_config->ssp_dai_rx_allocated = false; ++ ++ ssp_config->i2s_settings = ssp_platform_i2s_config; ++ pr_info("SSP DAI: FCT %s ssp_config %p\n", ++ __func__, ssp_config); ++ ++ cpu_dai->playback_dma_data = cpu_dai->capture_dma_data = ssp_config; ++ ++ return 0; ++ ++} /* ssp_probe */ ++ ++static int ssp_remove(struct snd_soc_dai *cpu_dai) ++{ ++ struct intel_ssp_config *ssp_config; ++ ++ WARN(!cpu_dai, "SSP DAI: " ++ "ERROR NULL cpu_dai\n"); ++ if (!cpu_dai) ++ return -EINVAL; ++ ++ ssp_config = cpu_dai->playback_dma_data; ++ ++ kfree(ssp_config); ++ ++ return 0; ++} /* ssp_remove */ ++ ++static int ssp_dai_startup(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *cpu_dai) ++{ ++ struct intel_ssp_config *ssp_config; ++ struct snd_pcm_runtime *pl_runtime; ++ struct intel_alsa_ssp_stream_info *str_info; ++ struct intel_ssp_info *ssp_info; ++ struct snd_soc_dai_driver *cpudai_drv = cpu_dai->driver; ++ unsigned int device; ++ int ret = 0; ++ ++ WARN(!cpu_dai->driver, "SSP DAI: " ++ "FCT %s ERROR NULL cpu_dai->driver\n", ++ __func__); ++ if (!cpu_dai->driver) ++ return -EINVAL; ++ ++ pr_info("SSP DAI: FCT %s enters for DAI Id = %d\n", ++ __func__, cpu_dai->driver->id); ++ ++ ssp_info = dev_get_drvdata(cpu_dai->dev); ++ ++ WARN(!ssp_info, "SSP DAI: ERROR NULL ssp_info\n"); ++ if (!ssp_info) ++ return -EINVAL; ++ ++ pl_runtime = substream->runtime; ++ ++ ssp_config = snd_soc_dai_get_dma_data(cpu_dai, substream); ++ ++ WARN(!ssp_config, "SSP DAI: " ++ "FCT %s ERROR NULL ssp_config\n", ++ __func__); ++ if (!ssp_config) ++ return -EINVAL; ++ ++ ++ device = cpu_dai->driver->id; ++ ++ /* ++ * setup the internal data structure stream pointers based on it being ++ * playback or capture stream ++ */ ++ str_info = kzalloc(sizeof(*str_info), GFP_KERNEL); ++ ++ if (!str_info) { ++ pr_err("SSP DAI: str_info alloc failure\n"); ++ return -EINVAL; ++ ++ } ++ str_info->substream = substream; ++ str_info->ssp_config = ssp_config; ++ str_info->stream_status = 0; ++ ++ INIT_WORK(&str_info->ssp_ws, intel_mid_ssp_transfer_data); ++ ++ /* ++ * Initialize SSPx [x=0,1] driver ++ * Store the Stream information ++ */ ++ pl_runtime->private_data = str_info; ++ ++ pr_debug("SSP DAI: FCT %s enters cpu_dai->card->name = %s\n", ++ __func__, cpu_dai->card->name); ++ ++ if (!cpu_dai->active) { ++ if (!strcmp(cpudai_drv->name, SSP_BT_DAI_NAME)) { ++ ssp_config->i2s_handle = ++ intel_mid_i2s_open(SSP_USAGE_BLUETOOTH_FM); ++ pr_debug("opening the CPU_DAI for "\ ++ "SSP_USAGE_BLUETOOTH_FM, i2s_handle = %p\n", ++ ssp_config->i2s_handle); ++ ++ } else if (!strcmp(cpudai_drv->name, SSP_MODEM_DAI_NAME)) { ++ ssp_config->i2s_handle = ++ intel_mid_i2s_open(SSP_USAGE_MODEM); ++ pr_debug("opening the CPU_DAI for "\ ++ "SSP_USAGE_MODEM, i2s_handle = %p\n", ++ ssp_config->i2s_handle); ++ ++ } else { ++ pr_err("non Valid SOC CARD\n"); ++ return -EINVAL; ++ } ++ ++ /* Set the Write Callback */ ++ ret = intel_mid_i2s_set_wr_cb(ssp_config->i2s_handle, ++ ssp_dma_complete); ++ if (ret) ++ return ret; ++ ++ /* Set the Default Read Callback */ ++ ret = intel_mid_i2s_set_rd_cb(ssp_config->i2s_handle, ++ ssp_dma_complete); ++ if (ret) ++ return ret; ++ ++ } else { ++ /* ++ * do nothing because already Open by sibling substream ++ */ ++ pr_debug("SSP DAI: FCT %s Open DO NOTHING\n", ++ __func__); ++ } ++ return 0; ++} /* ssp_dai_startup */ ++ ++static void ssp_dai_shutdown(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *cpu_dai) ++{ ++ struct intel_ssp_config *ssp_config; ++ struct intel_alsa_ssp_stream_info *str_info; ++ struct snd_pcm_runtime *runtime; ++ struct intel_ssp_info *ssp_info; ++ ++ ssp_config = snd_soc_dai_get_dma_data(cpu_dai, substream); ++ ++ BUG_ON(!ssp_config); ++ ++ runtime = substream->runtime; ++ BUG_ON(!runtime->private_data); ++ ++ str_info = runtime->private_data; ++ BUG_ON(!str_info); ++ ++ ssp_info = dev_get_drvdata(cpu_dai->dev); ++ BUG_ON(!ssp_info); ++ ++ /* Cancel pending work */ ++ cancel_work_sync(&str_info->ssp_ws); ++ ++ switch (substream->stream) { ++ case SNDRV_PCM_STREAM_PLAYBACK: ++ /* ++ * Only Free Tx channel if no playback streams are active ++ * Shutdown can be called right after a startup if something ++ * failed (as a concurrency issue ++ * so this case can happen ++ */ ++ if ((!cpu_dai->playback_active) && ++ (ssp_config->i2s_settings.ssp_active_tx_slots_map)) { ++ intel_mid_i2s_command(ssp_config->i2s_handle, ++ SSP_CMD_FREE_TX, NULL); ++ pr_debug("SSP DAI: FCT %s TX DMA Channel released\n", ++ __func__); ++ } ++ ssp_config->ssp_dai_tx_allocated = false; ++ break; ++ ++ case SNDRV_PCM_STREAM_CAPTURE: ++ /* ++ * Only Free Rx channel if no capture streams are active ++ */ ++ if ((!cpu_dai->capture_active) && ++ (ssp_config->i2s_settings.ssp_active_rx_slots_map)) { ++ intel_mid_i2s_command(ssp_config->i2s_handle, ++ SSP_CMD_FREE_RX, NULL); ++ pr_debug("SSP DAI: FCT %s RX DMA Channel released\n", ++ __func__); ++ } ++ ssp_config->ssp_dai_rx_allocated = false; ++ break; ++ ++ default: ++ pr_err("SSP DAI: FCT %s Bad stream_dir: %d\n", ++ __func__, substream->stream); ++ break; ++ } ++ ++#ifdef _LLI_ENABLED_ ++ clear_bit(INTEL_ALSA_SSP_STREAM_INIT, &str_info->stream_status); ++#endif /* _LLI_ENABLED_ */ ++ ++ kfree(str_info); ++ ++ if (!cpu_dai->active) { ++ pr_info("SSP DAI: FCT %s closing I2S\n", ++ __func__); ++ /* ++ * Close the Intel MID I2S connection ++ */ ++ intel_mid_i2s_close(ssp_config->i2s_handle); ++ ++ ssp_config->i2s_handle = NULL; ++#ifndef _LLI_ENABLED_ ++ ssp_config->intel_mid_dma_alloc = false; ++#endif /* _LLI_ENABLED_ */ ++ } ++ ++} /* ssp_dai_shutdown */ ++ ++static int ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, ++ unsigned int fmt) ++{ ++ struct intel_ssp_config *ssp_config; ++ struct intel_mid_i2s_settings *i2s_config; ++ ++ ssp_config = cpu_dai->playback_dma_data; ++ ++ WARN(!ssp_config, "SSP DAI: FCT %s ssp_config=NULL\n", ++ __func__); ++ if (!ssp_config) ++ return -EINVAL; ++ ++ pr_debug("SSP DAI: FCT %s fmt = %d\n", ++ __func__, fmt); ++ ++ i2s_config = &(ssp_config->i2s_settings); ++ ++ /* ++ * SSP CLK Direction ++ * SSP FRMSYNC Direction ++ */ ++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { ++ case SND_SOC_DAIFMT_CBM_CFM: ++ i2s_config->sspslclk_direction = SSPSCLK_MASTER_MODE; ++ i2s_config->sspsfrm_direction = SSPSCLK_MASTER_MODE; ++ /* ++ * Mandatory to be able to perform only RX without TX ++ * in SSP CLK Master Mode ++ * ++ */ ++ i2s_config->ssp_duplex_mode = RX_WITHOUT_TX_MODE; ++ break; ++ case SND_SOC_DAIFMT_CBM_CFS: ++ i2s_config->sspslclk_direction = SSPSCLK_MASTER_MODE; ++ i2s_config->sspsfrm_direction = SSPSCLK_SLAVE_MODE; ++ /* ++ * Mandatory to be able to perform only RX without TX ++ * in SSP CLK Master Mode ++ * ++ */ ++ i2s_config->ssp_duplex_mode = RX_WITHOUT_TX_MODE; ++ break; ++ case SND_SOC_DAIFMT_CBS_CFS: ++ i2s_config->sspslclk_direction = SSPSCLK_SLAVE_MODE; ++ i2s_config->sspsfrm_direction = SSPSCLK_SLAVE_MODE; ++ i2s_config->ssp_duplex_mode = RX_AND_TX_MODE; ++ break; ++ case SND_SOC_DAIFMT_CBS_CFM: ++ i2s_config->sspslclk_direction = SSPSCLK_SLAVE_MODE; ++ i2s_config->sspsfrm_direction = SSPSCLK_MASTER_MODE; ++ i2s_config->ssp_duplex_mode = RX_AND_TX_MODE; ++ break; ++ default: ++ pr_err("SSP DAI: %s Bad DAI CLK/FS Mode=%d\n", ++ __func__, ++ (fmt & SND_SOC_DAIFMT_MASTER_MASK)); ++ return -EINVAL; ++ } ++ /* ++ * SSP Sgnal Inversion Mode ++ * Use clock gating bitfield for ++ * Serial bit-rate Clock Mode ++ */ ++ switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) { ++ case SSP_DAI_SCMODE_0: ++ i2s_config->ssp_serial_clk_mode = SSP_CLK_MODE_0; ++ break; ++ case SSP_DAI_SCMODE_1: ++ i2s_config->ssp_serial_clk_mode = SSP_CLK_MODE_1; ++ break; ++ case SSP_DAI_SCMODE_2: ++ i2s_config->ssp_serial_clk_mode = SSP_CLK_MODE_2; ++ break; ++ case SSP_DAI_SCMODE_3: ++ i2s_config->ssp_serial_clk_mode = SSP_CLK_MODE_3; ++ break; ++ default: ++ pr_err("SSP DAI: %s Bad DAI Signal Inversion Mode=%d\n", ++ __func__, ++ (fmt & SND_SOC_DAIFMT_INV_MASK)); ++ return -EINVAL; ++ } ++ ++ /* ++ * SSP FS Inversion Mode ++ */ ++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { ++ case SND_SOC_DAIFMT_NB_NF: ++ case SND_SOC_DAIFMT_IB_NF: ++ i2s_config->ssp_frmsync_pol_bit = SSP_FRMS_ACTIVE_HIGH; ++ break; ++ case SND_SOC_DAIFMT_NB_IF: ++ case SND_SOC_DAIFMT_IB_IF: ++ i2s_config->ssp_frmsync_pol_bit = SSP_FRMS_ACTIVE_LOW; ++ break; ++ default: ++ pr_err("SSP DAI: %s Bad DAI FS Inversion Mode=%d\n", ++ __func__, ++ (fmt & SND_SOC_DAIFMT_INV_MASK)); ++ return -EINVAL; ++ } ++ ++ /* ++ * SSP Format Mode ++ */ ++ ++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { ++ case SND_SOC_DAIFMT_I2S: ++ i2s_config->frame_format = PSP_FORMAT; ++ break; ++ ++ default: ++ pr_err("SSP DAI: %s Bad DAI format Mode=%d\n", ++ __func__, ++ (fmt & SND_SOC_DAIFMT_FORMAT_MASK)); ++ return -EINVAL; ++ } ++ return 0; ++} /* ssp_set_dai_fmt */ ++ ++static int ssp_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, ++ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) ++{ ++ struct intel_ssp_config *ssp_config; ++ struct intel_mid_i2s_settings *i2s_config; ++ ++ ssp_config = cpu_dai->playback_dma_data; ++ ++ WARN(!ssp_config, "SSP DAI: FCT %s ssp_config=NULL\n", ++ __func__); ++ if (!ssp_config) ++ return -EINVAL; ++ ++ ++ i2s_config = &(ssp_config->i2s_settings); ++ ++ i2s_config->frame_rate_divider_control = slots; ++ i2s_config->data_size = slot_width; ++ i2s_config->mode = SSP_IN_NETWORK_MODE; ++ i2s_config->ssp_active_tx_slots_map = tx_mask; ++ i2s_config->ssp_active_rx_slots_map = rx_mask; ++ ++ pr_debug("i2s_config->frame_rate_divider_control = %d\n", ++ i2s_config->frame_rate_divider_control); ++ pr_debug("i2s_config->data_size = %d\n", ++ i2s_config->data_size); ++ pr_debug("i2s_config->mode = %d\n", ++ i2s_config->mode); ++ pr_debug("i2s_config->ssp_active_tx_slots_map = %d\n", ++ i2s_config->ssp_active_tx_slots_map); ++ pr_debug("i2s_config->ssp_active_rx_slots_map = %d\n", ++ i2s_config->ssp_active_rx_slots_map); ++ ++ return 0; ++} ++ ++static int ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, ++ int clk_id, unsigned int freq, int dir) ++{ ++ struct intel_ssp_config *ssp_config; ++ struct intel_mid_i2s_settings *i2s_config; ++ ++ ssp_config = cpu_dai->playback_dma_data; ++ ++ BUG_ON(!ssp_config); ++ ++ i2s_config = &(ssp_config->i2s_settings); ++ ++ pr_debug("SSP DAI: FCT %s clk_id = %d\n", ++ __func__, clk_id); ++ ++ switch (clk_id) { ++ case SSP_CLK_ONCHIP: ++ i2s_config->master_mode_clk_selection = SSP_ONCHIP_CLOCK; ++ break; ++ case SSP_CLK_NET: ++ i2s_config->master_mode_clk_selection = SSP_NETWORK_CLOCK; ++ break; ++ case SSP_CLK_EXT: ++ i2s_config->master_mode_clk_selection = SSP_EXTERNAL_CLOCK; ++ break; ++ case SSP_CLK_AUDIO: ++ i2s_config->master_mode_clk_selection = SSP_ONCHIP_AUDIO_CLOCK; ++ break; ++ default: ++ i2s_config->master_mode_standard_freq = ++ SSP_MASTER_CLOCK_UNDEFINED; ++ pr_err("SSP DAI: %s Bad clk_id=%d\n", ++ __func__, ++ clk_id); ++ return -EINVAL; ++ } ++ ++ pr_debug("SSP DAI:FCT %s freq = %d\n", ++ __func__, freq); ++ ++ switch (freq) { ++ case 8000: ++ i2s_config->master_mode_standard_freq = SSP_FRM_FREQ_8_000; ++ i2s_config->ssp_psp_T1 = 0; ++ i2s_config->ssp_psp_T2 = 1; ++ i2s_config->ssp_psp_T4 = 0; ++ i2s_config->ssp_psp_T5 = 0; ++ i2s_config->ssp_psp_T6 = 1; ++ break; ++ ++ case 11025: ++ i2s_config->master_mode_standard_freq = SSP_FRM_FREQ_11_025; ++ pr_err("SSP DAI: %s Bad freq_out=%d\n", ++ __func__, ++ freq); ++ return -EINVAL; ++ ++ case 16000: ++ i2s_config->master_mode_standard_freq = SSP_FRM_FREQ_16_000; ++ i2s_config->ssp_psp_T1 = 6; ++ i2s_config->ssp_psp_T2 = 2; ++ i2s_config->ssp_psp_T4 = 0; ++ i2s_config->ssp_psp_T5 = 14; ++ i2s_config->ssp_psp_T6 = 16; ++ break; ++ ++ case 22050: ++ i2s_config->master_mode_standard_freq = SSP_FRM_FREQ_22_050; ++ pr_err("SSP DAI: %s Bad freq_out=%d\n", ++ __func__, ++ freq); ++ return -EINVAL; ++ ++ case 44100: ++ i2s_config->master_mode_standard_freq = SSP_FRM_FREQ_44_100; ++ pr_err("SSP DAI: %s Bad freq_out=%d\n", ++ __func__, ++ freq); ++ return -EINVAL; ++ ++ case 48000: ++ i2s_config->master_mode_standard_freq = SSP_FRM_FREQ_48_000; ++ i2s_config->ssp_psp_T1 = 6; ++ i2s_config->ssp_psp_T2 = 2; ++ i2s_config->ssp_psp_T4 = 0; ++ i2s_config->ssp_psp_T5 = 14; ++ i2s_config->ssp_psp_T6 = 16; ++ break; ++ ++ default: ++ i2s_config->master_mode_standard_freq = SSP_FRM_FREQ_UNDEFINED; ++ pr_err("SSP DAI: %s Bad freq_out=%d\n", ++ __func__, ++ freq); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int ssp_set_dai_tristate(struct snd_soc_dai *cpu_dai, ++ int tristate) ++{ ++ struct intel_ssp_config *ssp_config; ++ struct intel_mid_i2s_settings *i2s_config; ++ ++ ssp_config = cpu_dai->playback_dma_data; ++ ++ BUG_ON(!ssp_config); ++ ++ i2s_config = &(ssp_config->i2s_settings); ++ ++ if (IS_TRISTATE_ENABLED(tristate)) ++ i2s_config->tx_tristate_enable = TXD_TRISTATE_ON; ++ else ++ i2s_config->tx_tristate_enable = TXD_TRISTATE_OFF; ++ ++ if (IS_NEXT_FRMS_ASSERTED_WITH_LSB_PREVIOUS_FRM(tristate)) ++ i2s_config->ssp_frmsync_timing_bit = ++ NEXT_FRMS_ASS_WITH_LSB_PREVIOUS_FRM; ++ else ++ i2s_config->ssp_frmsync_timing_bit = ++ NEXT_FRMS_ASS_AFTER_END_OF_T4; ++ ++ pr_debug("FCT %s tristate %x\n", __func__, tristate); ++ ++ return 0; ++} ++ ++/** ++ * ssp_dai_trigger- stream activities are handled here ++ * This function is called whenever a stream activity is invoked ++ * The Trigger function is called in an atomic context ++ * ++ * @param substream Substream for which the stream function is called ++ * @param cmd The stream command thats requested from upper layer ++ * return status 0 ==> OK ++ * ++ */ ++static int ssp_dai_trigger(struct snd_pcm_substream *substream, ++ int cmd, struct snd_soc_dai *cpu_dai) ++{ ++ int ret_val = 0; ++ struct intel_alsa_ssp_stream_info *str_info; ++ struct snd_pcm_runtime *pl_runtime; ++ struct intel_ssp_info *ssp_info; ++#ifdef _LLI_ENABLED_ ++ struct intel_ssp_config *ssp_config; ++#endif /* _LLI_ENABLED_ */ ++ ++ bool trigger_start = true; ++ int stream = 0; ++ ++ pr_debug("SSP DAI: FCT %s enters\n", ++ __func__); ++ ++ stream = substream->stream; ++ ++ ++ pl_runtime = substream->runtime; ++ ++ WARN(!pl_runtime->private_data, "SSP DAI: ERROR " ++ "NULL pl_runtime->private_data\n"); ++ if (!pl_runtime->private_data) ++ return -EINVAL; ++ ++ WARN(!cpu_dai, "SSP DAI: ERROR NULL cpu_dai\n"); ++ if (!cpu_dai) ++ return -EINVAL; ++ ++ WARN(!cpu_dai->dev, "SSP DAI: ERROR NULL cpu_dai->dev\n"); ++ if (!cpu_dai->dev) ++ return -EINVAL; ++ ++ ssp_info = dev_get_drvdata(cpu_dai->dev); ++ ++ ++ WARN(!ssp_info->ssp_dai_wq, "SSP DAI: ERROR NULL ssp_dai_wq\n"); ++ if (!ssp_info->ssp_dai_wq) ++ return -EINVAL; ++ ++ str_info = pl_runtime->private_data; ++ ++#ifdef _LLI_ENABLED_ ++ ssp_config = str_info->ssp_config; ++ ++ WARN(!ssp_config, "SSP DAI: ERROR NULL ssp_config\n"); ++ if (!ssp_config) ++ return -EINVAL; ++#endif /* _LLI_ENABLED_ */ ++ ++ pr_debug("SSP DAI: FCT %s CMD = 0x%04X\n", ++ __func__, cmd); ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ if (!test_and_set_bit(INTEL_ALSA_SSP_STREAM_STARTED, ++ &str_info->stream_status)) { ++#ifndef _LLI_ENABLED_ ++ if (test_bit(INTEL_ALSA_SSP_STREAM_DROPPED, ++ &str_info->stream_status)) { ++ pr_debug("SSP DAI: FCT %s do not restart the trigger stream running already\n", ++ __func__); ++ trigger_start = false; ++ } else ++#endif /* _LLI_ENABLED_ */ ++ trigger_start = true; ++ } else { ++ pr_err("SSP DAI: ERROR 2 consecutive TRIGGER_START\n"); ++ return -EBUSY; ++ } ++ ++ /* Store the substream locally */ ++ if (trigger_start) { ++ if (stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ ++ pr_debug("SSP DAI: queue Playback Work\n"); ++ queue_work(ssp_info->ssp_dai_wq, ++ &str_info->ssp_ws); ++ } else if (stream == SNDRV_PCM_STREAM_CAPTURE) { ++ ++ pr_debug("SSP DAI: queue Capture Work\n"); ++ queue_work(ssp_info->ssp_dai_wq, ++ &str_info->ssp_ws); ++ } else { ++ pr_err("SSP DAI: SNDRV_PCM_TRIGGER_START Bad Stream: %d\n", ++ substream->stream); ++ return -EINVAL; ++ } ++ } ++ break; ++ ++ case SNDRV_PCM_TRIGGER_STOP: ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ if (test_and_clear_bit(INTEL_ALSA_SSP_STREAM_STARTED, ++ &str_info->stream_status)) { ++#ifdef _LLI_ENABLED_ ++ intel_mid_i2s_command(ssp_config->i2s_handle, ++ (stream == SNDRV_PCM_STREAM_PLAYBACK) ? ++ SSP_CMD_DISABLE_DMA_TX_INTR : ++ SSP_CMD_DISABLE_DMA_RX_INTR, ++ NULL); ++#else ++ set_bit(INTEL_ALSA_SSP_STREAM_DROPPED, ++ &str_info->stream_status); ++#endif /* _LLI_ENABLED_ */ ++ } else { ++ pr_err("SSP DAI: trigger START/STOP mismatch\n"); ++ return -EBUSY; ++ } ++ break; ++ ++ default: ++ pr_err("SSP DAI: snd_i2s_alsa_pcm_trigger Bad Command\n"); ++ return -EINVAL; ++ } ++ return ret_val; ++} /* ssp_dai_trigger */ ++ ++/** ++ * ssp_dai_hw_params - Allocate memory for Ring Buffer according ++ * to hw_params. ++ * It's called in a non-atomic context ++ * ++ * @param substream Substream for which the stream function is called ++ * @param hw_params Stream command thats requested from upper layer ++ * @param cpu_dai Pointer to the CPU DAI that is used ++ * return status 0 ==> OK ++ * ++ */ ++static int ssp_dai_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *hw_params, ++ struct snd_soc_dai *cpu_dai) ++{ ++ ++ return 0; ++} ++ ++static int ssp_dai_prepare(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *cpu_dai) ++{ ++ struct intel_ssp_config *ssp_config; ++ struct intel_ssp_info *ssp_info; ++ ++ pr_debug("SSP DAI: FCT %s enters\n", ++ __func__); ++ ++ WARN(!cpu_dai, "SSP DAI: ERROR NULL cpu_dai\n"); ++ if (!cpu_dai) ++ return -EINVAL; ++ ++ ssp_info = dev_get_drvdata(cpu_dai->dev); ++ WARN(!ssp_info, "SSP DAI: ERROR NULL ssp_info\n"); ++ if (!ssp_info) ++ return -EINVAL; ++ ++ ssp_config = snd_soc_dai_get_dma_data(cpu_dai, substream); ++ pr_debug("SSP DAI: FCT %s ssp_dai_tx_allocated %d "\ ++ "ssp_dai_rx_allocated %d\n", ++ __func__, ++ ssp_config->ssp_dai_tx_allocated, ++ ssp_config->ssp_dai_rx_allocated); ++ ++ /* ++ * The set HW Config is only once for a CPU DAI ++ */ ++ ++ if (!ssp_config->ssp_dai_tx_allocated && ++ !ssp_config->ssp_dai_rx_allocated) { ++ intel_mid_i2s_command(ssp_config->i2s_handle, ++ SSP_CMD_SET_HW_CONFIG, ++ &(ssp_config->i2s_settings)); ++ } ++ ++ switch (substream->stream) { ++ case SNDRV_PCM_STREAM_PLAYBACK: ++ if (!ssp_config->ssp_dai_tx_allocated) { ++ if (intel_mid_i2s_command(ssp_config->i2s_handle, ++ SSP_CMD_ALLOC_TX, NULL)) { ++ pr_err("can not alloc TX DMA Channel\n"); ++ return -EBUSY; ++ } ++ ssp_config->ssp_dai_tx_allocated = true; ++ } ++ break; ++ ++ case SNDRV_PCM_STREAM_CAPTURE: ++ if (!ssp_config->ssp_dai_rx_allocated) { ++ if (intel_mid_i2s_command(ssp_config->i2s_handle, ++ SSP_CMD_ALLOC_RX, NULL)) { ++ pr_err("can not alloc RX DMA Channel\n"); ++ return -EBUSY; ++ } ++ ssp_config->ssp_dai_rx_allocated = true; ++ } ++ break; ++ ++ default: ++ pr_err("SSP DAI: FCT %s Bad stream_dir: %d\n", ++ __func__, substream->stream); ++ return -EINVAL; ++ } ++ ++#ifndef _LLI_ENABLED_ ++ ssp_config->intel_mid_dma_alloc = true; ++#endif /* _LLI_ENABLED_ */ ++ ++ pr_debug("SSP DAI: FCT %s leaves\n", ++ __func__); ++ ++ return 0; ++} ++ ++ ++ ++/* BT/FM */ ++static struct snd_soc_dai_ops ssp_dai_ops = { ++ .startup = ssp_dai_startup, ++ .shutdown = ssp_dai_shutdown, ++ .trigger = ssp_dai_trigger, ++ .hw_params = ssp_dai_hw_params, ++ .prepare = ssp_dai_prepare, ++ .set_sysclk = ssp_set_dai_sysclk, ++ .set_pll = NULL, ++ .set_fmt = ssp_set_dai_fmt, ++ .set_tdm_slot = ssp_set_dai_tdm_slot, ++ .set_tristate = ssp_set_dai_tristate, ++}; ++ ++#define SSP_SUPPORTED_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \ ++ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \ ++ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) ++ ++#define SSP_SUPPORTED_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ ++ SNDRV_PCM_FMTBIT_U16_LE | \ ++ SNDRV_PCM_FMTBIT_S8 | \ ++ SNDRV_PCM_FMTBIT_U8) ++ ++struct snd_soc_dai_driver intel_ssp_platform_dai[] = { ++{ ++ .name = SSP_MODEM_DAI_NAME, ++ .id = 0, ++ .playback = { ++ .channels_min = 1, ++ .channels_max = 8, ++ .rates = SSP_SUPPORTED_RATES, ++ .formats = SSP_SUPPORTED_FORMATS, ++ }, ++ .capture = { ++ .channels_min = 1, ++ .channels_max = 8, ++ .rates = SSP_SUPPORTED_RATES, ++ .formats = SSP_SUPPORTED_FORMATS, ++ }, ++ .ops = &ssp_dai_ops, ++ .probe = ssp_probe, ++ .remove = ssp_remove, ++}, ++{ ++ .name = SSP_BT_DAI_NAME, ++ .id = 1, ++ .playback = { ++ .channels_min = 1, ++ .channels_max = 8, ++ .rates = SSP_SUPPORTED_RATES, ++ .formats = SSP_SUPPORTED_FORMATS, ++ }, ++ .capture = { ++ .channels_min = 1, ++ .channels_max = 8, ++ .rates = SSP_SUPPORTED_RATES, ++ .formats = SSP_SUPPORTED_FORMATS, ++ }, ++ .ops = &ssp_dai_ops, ++ .probe = ssp_probe, ++ .remove = ssp_remove, ++}, ++}; ++ ++static const struct snd_soc_component_driver ssp_component = { ++ .name = "ssp", ++}; ++ ++static int ssp_dai_probe(struct platform_device *pdev) ++{ ++ int ret; ++ struct intel_ssp_info *ssp_info; ++ ++ pr_info("SSP DAI: FCT %s enters\n", ++ __func__); ++ ++ ssp_info = kzalloc(sizeof(struct intel_ssp_info), GFP_KERNEL); ++ ++ if (ssp_info == NULL) { ++ pr_err("Unable to allocate ssp_info\n"); ++ return -ENOMEM; ++ } ++ pr_info("ssp_info address %p", ssp_info); ++ ++ ret = snd_soc_register_platform(&pdev->dev, ++ &soc_ssp_platform_drv); ++ if (ret) { ++ pr_err("registering SSP PLATFORM failed\n"); ++ snd_soc_unregister_component(&pdev->dev); ++ kfree(ssp_info); ++ return -EBUSY; ++ } ++ ++ ret = snd_soc_register_component(&pdev->dev, &ssp_component, ++ intel_ssp_platform_dai, ++ ARRAY_SIZE(intel_ssp_platform_dai)); ++ ++ if (ret) { ++ pr_err("registering cpu DAIs failed\n"); ++ snd_soc_unregister_component(&pdev->dev); ++ kfree(ssp_info); ++ return -EBUSY; ++ } ++ ++ ssp_info->ssp_dai_wq = create_workqueue("ssp_transfer_data"); ++ ++ if (!ssp_info->ssp_dai_wq) { ++ pr_err("work queue failed\n"); ++ snd_soc_unregister_component(&pdev->dev); ++ kfree(ssp_info); ++ return -ENOMEM; ++ } ++ ++ platform_set_drvdata(pdev, ssp_info); ++ ++ pr_info("SSP DAI: FCT %s leaves %d\n", ++ __func__, ret); ++ ++ return ret; ++} ++ ++static int ssp_dai_remove(struct platform_device *pdev) ++{ ++ struct intel_ssp_info *ssp_info = platform_get_drvdata(pdev); ++ ++ pr_debug("SSP DAI: FCT %s enters\n", ++ __func__); ++ ++ if (ssp_info == NULL) { ++ pr_err("Unable to allocate ssp_info\n"); ++ return -ENOMEM; ++ } ++ pr_info("ssp_info address %p", ssp_info); ++ ++ flush_workqueue(ssp_info->ssp_dai_wq); ++ ++ destroy_workqueue(ssp_info->ssp_dai_wq); ++ ++ platform_set_drvdata(pdev, NULL); ++ ++ snd_soc_unregister_component(&pdev->dev); ++ ++ snd_soc_unregister_platform(&pdev->dev); ++ ++ pr_debug("SSP DAI: FCT %s leaves\n", ++ __func__); ++ ++ return 0; ++} ++ ++static struct platform_driver intel_ssp_dai_driver = { ++ .driver = { ++ .name = "mid-ssp-dai", ++ .owner = THIS_MODULE, ++ }, ++ .probe = ssp_dai_probe, ++ .remove = ssp_dai_remove, ++}; ++ ++ ++static int __init ssp_soc_dai_init(void) ++{ ++ pr_info("SSP DAI: FCT %s called\n", ++ __func__); ++ ++ return platform_driver_register(&intel_ssp_dai_driver); ++} ++module_init(ssp_soc_dai_init); ++ ++static void __exit ssp_soc_dai_exit(void) ++{ ++ pr_debug("SSP DAI: FCT %s called\n", ++ __func__); ++ ++ platform_driver_unregister(&intel_ssp_dai_driver); ++ ++} ++module_exit(ssp_soc_dai_exit); ++ ++MODULE_DESCRIPTION("ASoC Intel(R) MID Platform driver"); ++MODULE_AUTHOR("Selma Bensaid"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:ssp-cpu-dai"); +diff --git a/sound/soc/intel/ssp/mid_ssp.h b/sound/soc/intel/ssp/mid_ssp.h +new file mode 100644 +index 0000000..4df4eac +--- /dev/null ++++ b/sound/soc/intel/ssp/mid_ssp.h +@@ -0,0 +1,124 @@ ++/* ++ * mfld_ssp.h - ASoC CPU DAI driver for ++ * ++ * Copyright (C) 2011-12 Intel Corp ++ * Authors: Selma Bensaid ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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 MID_SSP_H_ ++#define MID_SSP_H_ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define SSP_MODEM_DAI_NAME "ssp-modem-cpu-dai" ++#define SSP_BT_DAI_NAME "ssp-bt-cpu-dai" ++ ++#define SSP_MAX_BUFFER (640*1024) ++#define SSP_MIN_BUFFER (640*1024) ++ ++#define TRISTATE_BIT 0 ++#define FRAME_SYNC_RELATIVE_TIMING_BIT 1 ++#define DUMMY_START_ONE_PERIOD_OFFSET 2 ++#define DUMMY_START_ONE_PERIOD_MASK 0x3 ++ ++#define IS_TRISTATE_ENABLED(x) (x & BIT(TRISTATE_BIT)) ++#define IS_NEXT_FRMS_ASSERTED_WITH_LSB_PREVIOUS_FRM(x) \ ++ ((x & BIT(FRAME_SYNC_RELATIVE_TIMING_BIT)) \ ++ >> FRAME_SYNC_RELATIVE_TIMING_BIT) ++#define IS_DUMMY_START_ONE_PERIOD_OFFSET(x) \ ++ ((x >> DUMMY_START_ONE_PERIOD_OFFSET) \ ++ & DUMMY_START_ONE_PERIOD_MASK) ++ ++#define MID_SSP_RX_FIFO_THRESHOLD 8 ++#define MID_SSP_TX_FIFO_THRESHOLD 7 ++ ++ ++/* data driven FALLING, data sampled RISING, idle LOW */ ++#define SSP_DAI_SCMODE_0 (1 << 4) ++/* data driven RISING, data sampled FALLING, idle LOW */ ++#define SSP_DAI_SCMODE_1 (2 << 4) ++/* data driven RISING, data sampled FALLING, idle HIGH */ ++#define SSP_DAI_SCMODE_2 (3 << 4) ++/* data driven FALLING, data sampled RISING, idle HIGH */ ++#define SSP_DAI_SCMODE_3 (4 << 4) ++ ++ ++/* ++ * Structures Definition ++ */ ++ ++ ++struct intel_ssp_config { ++ struct intel_mid_i2s_hdl *i2s_handle; ++ struct intel_mid_i2s_settings i2s_settings; ++#ifndef _LLI_ENABLED_ ++ bool intel_mid_dma_alloc; ++#endif /* _LLI_ENABLED_ */ ++ bool ssp_dai_tx_allocated; ++ bool ssp_dai_rx_allocated; ++}; ++ ++struct intel_ssp_info { ++ struct workqueue_struct *ssp_dai_wq; ++}; ++ ++struct intel_alsa_ssp_stream_info { ++ struct snd_pcm_substream *substream; ++ struct work_struct ssp_ws; ++ struct intel_ssp_config *ssp_config; ++ unsigned long stream_status; ++ u32 period_req_index; ++ s32 period_cb_index; ++ u8 *addr; ++ int length; ++}; ++ ++ ++/* ++ * Enum Definition ++ */ ++ ++enum intel_alsa_ssp_stream_status { ++ INTEL_ALSA_SSP_STREAM_INIT = 0, ++ INTEL_ALSA_SSP_STREAM_STARTED, ++ INTEL_ALSA_SSP_STREAM_RUNNING, ++ INTEL_ALSA_SSP_STREAM_PAUSED, ++ INTEL_ALSA_SSP_STREAM_DROPPED, ++}; ++enum ssp_clk_def { ++ SSP_CLK_ONCHIP = 0x0, ++ SSP_CLK_NET, ++ SSP_CLK_EXT, ++ SSP_CLK_AUDIO ++}; ++ ++ ++#endif /* MID_SSP_H_ */ +diff --git a/sound/soc/intel/sst/Makefile b/sound/soc/intel/sst/Makefile +new file mode 100644 +index 0000000..8b620d0 +--- /dev/null ++++ b/sound/soc/intel/sst/Makefile +@@ -0,0 +1,13 @@ ++# Makefile for SST Audio driver ++snd-intel-sst-objs := sst.o sst_ipc.o sst_stream.o sst_drv_interface.o sst_dsp.o sst_pvt.o sst_app_interface.o sst_acpi.o ++ ++ifdef CONFIG_DEBUG_FS ++ snd-intel-sst-objs += sst_debug.o ++endif ++ ++obj-$(CONFIG_SND_INTEL_SST) += snd-intel-sst.o ++ ++ ++CFLAGS_snd-intel-sst.o = -I$(src) ++ ++ccflags-y += -DMRFLD_WORD_WA -Werror +diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/sst/sst.c +new file mode 100644 +index 0000000..0f59e47 +--- /dev/null ++++ b/sound/soc/intel/sst/sst.c +@@ -0,0 +1,1255 @@ ++/* ++ * sst.c - Intel SST Driver for audio engine ++ * ++ * Copyright (C) 2008-10 Intel Corp ++ * Authors: Vinod Koul ++ * Harsha Priya ++ * Dharageswari R ++ * KP Jeeja ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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. ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * This driver enumerates the SST audio engine as a PCI or ACPI device and ++ * provides interface to the platform driver to interact with the SST audio ++ * Firmware. ++ * ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "../sst_platform.h" ++#include "../platform_ipc_v2.h" ++#include "sst.h" ++ ++#define CREATE_TRACE_POINTS ++#include "sst_trace.h" ++ ++MODULE_AUTHOR("Vinod Koul "); ++MODULE_AUTHOR("Harsha Priya "); ++MODULE_AUTHOR("Dharageswari R "); ++MODULE_AUTHOR("KP Jeeja "); ++MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine Driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_VERSION(SST_DRIVER_VERSION); ++ ++struct intel_sst_drv *sst_drv_ctx; ++static struct mutex drv_ctx_lock; ++ ++/* ++ * * ioctl32 compat ++ * */ ++#ifdef CONFIG_COMPAT ++#include "sst_app_compat_interface.c" ++#else ++#define intel_sst_ioctl_compat NULL ++#endif ++ ++static const struct file_operations intel_sst_fops_cntrl = { ++ .owner = THIS_MODULE, ++ .open = intel_sst_open_cntrl, ++ .release = intel_sst_release_cntrl, ++ .unlocked_ioctl = intel_sst_ioctl, ++ .compat_ioctl = intel_sst_ioctl_compat, ++}; ++ ++struct miscdevice lpe_ctrl = { ++ .minor = MISC_DYNAMIC_MINOR,/* dynamic allocation */ ++ .name = "intel_sst_ctrl",/* /dev/intel_sst_ctrl */ ++ .fops = &intel_sst_fops_cntrl ++}; ++ ++static inline void set_imr_interrupts(struct intel_sst_drv *ctx, bool enable) ++{ ++ union interrupt_reg imr; ++ ++ spin_lock(&ctx->ipc_spin_lock); ++ imr.full = sst_shim_read(ctx->shim, SST_IMRX); ++ if (enable) { ++ imr.part.done_interrupt = 0; ++ imr.part.busy_interrupt = 0; ++ } else { ++ imr.part.done_interrupt = 1; ++ imr.part.busy_interrupt = 1; ++ } ++ sst_shim_write(ctx->shim, SST_IMRX, imr.full); ++ spin_unlock(&ctx->ipc_spin_lock); ++} ++ ++#define SST_IS_PROCESS_REPLY(header) ((header & PROCESS_MSG) ? true : false) ++#define SST_VALIDATE_MAILBOX_SIZE(size) ((size <= SST_MAILBOX_SIZE) ? true : false) ++ ++static irqreturn_t intel_sst_interrupt_mrfld(int irq, void *context) ++{ ++ union interrupt_reg_mrfld isr; ++ union ipc_header_mrfld header; ++ union sst_imr_reg_mrfld imr; ++ struct ipc_post *msg = NULL; ++ unsigned int size = 0; ++ struct intel_sst_drv *drv = (struct intel_sst_drv *) context; ++ irqreturn_t retval = IRQ_HANDLED; ++ ++ /* Interrupt arrived, check src */ ++ isr.full = sst_shim_read64(drv->shim, SST_ISRX); ++ if (isr.part.done_interrupt) { ++ /* Clear done bit */ ++ spin_lock(&drv->ipc_spin_lock); ++ header.full = sst_shim_read64(drv->shim, ++ drv->ipc_reg.ipcx); ++ header.p.header_high.part.done = 0; ++ sst_shim_write64(drv->shim, drv->ipc_reg.ipcx, header.full); ++ /* write 1 to clear status register */; ++ isr.part.done_interrupt = 1; ++ sst_shim_write64(drv->shim, SST_ISRX, isr.full); ++ spin_unlock(&drv->ipc_spin_lock); ++ trace_sst_ipc("ACK <-", header.p.header_high.full, ++ header.p.header_low_payload, ++ header.p.header_high.part.drv_id); ++ queue_work(drv->post_msg_wq, &drv->ipc_post_msg.wq); ++ retval = IRQ_HANDLED; ++ } ++ if (isr.part.busy_interrupt) { ++ spin_lock(&drv->ipc_spin_lock); ++ imr.full = sst_shim_read64(drv->shim, SST_IMRX); ++ imr.part.busy_interrupt = 1; ++ sst_shim_write64(drv->shim, SST_IMRX, imr.full); ++ spin_unlock(&drv->ipc_spin_lock); ++ header.full = sst_shim_read64(drv->shim, drv->ipc_reg.ipcd); ++ if (sst_create_ipc_msg(&msg, header.p.header_high.part.large)) { ++ pr_err("No memory available\n"); ++ drv->ops->clear_interrupt(); ++ return IRQ_HANDLED; ++ } ++ if (header.p.header_high.part.large) { ++ size = header.p.header_low_payload; ++ if (SST_VALIDATE_MAILBOX_SIZE(size)) { ++ memcpy_fromio(msg->mailbox_data, ++ drv->mailbox + drv->mailbox_recv_offset, size); ++ } else { ++ pr_err("Mailbox not copied, payload siz is: %u\n", size); ++ header.p.header_low_payload = 0; ++ } ++ } ++ msg->mrfld_header = header; ++ msg->is_process_reply = ++ SST_IS_PROCESS_REPLY(header.p.header_high.part.msg_id); ++ trace_sst_ipc("REPLY <-", msg->mrfld_header.p.header_high.full, ++ msg->mrfld_header.p.header_low_payload, ++ msg->mrfld_header.p.header_high.part.drv_id); ++ spin_lock(&drv->rx_msg_lock); ++ list_add_tail(&msg->node, &drv->rx_list); ++ spin_unlock(&drv->rx_msg_lock); ++ drv->ops->clear_interrupt(); ++ retval = IRQ_WAKE_THREAD; ++ } ++ return retval; ++} ++ ++static irqreturn_t intel_sst_irq_thread_mfld(int irq, void *context) ++{ ++ struct intel_sst_drv *drv = (struct intel_sst_drv *) context; ++ struct ipc_post *__msg, *msg = NULL; ++ unsigned long irq_flags; ++ ++ if (list_empty(&drv->rx_list)) ++ return IRQ_HANDLED; ++ ++ spin_lock_irqsave(&drv->rx_msg_lock, irq_flags); ++ list_for_each_entry_safe(msg, __msg, &drv->rx_list, node) { ++ ++ list_del(&msg->node); ++ spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags); ++ if (msg->is_process_reply) ++ drv->ops->process_message(msg); ++ else ++ drv->ops->process_reply(msg); ++ ++ if (msg->is_large) ++ kfree(msg->mailbox_data); ++ kfree(msg); ++ spin_lock_irqsave(&drv->rx_msg_lock, irq_flags); ++ } ++ spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags); ++ return IRQ_HANDLED; ++} ++/** ++* intel_sst_interrupt - Interrupt service routine for SST ++* ++* @irq: irq number of interrupt ++* @context: pointer to device structre ++* ++* This function is called by OS when SST device raises ++* an interrupt. This will be result of write in IPC register ++* Source can be busy or done interrupt ++*/ ++static irqreturn_t intel_sst_intr_mfld(int irq, void *context) ++{ ++ union interrupt_reg isr; ++ union ipc_header header; ++ irqreturn_t retval = IRQ_HANDLED; ++ struct ipc_post *msg = NULL; ++ unsigned int size = 0; ++ struct intel_sst_drv *drv = (struct intel_sst_drv *) context; ++ ++ /* Interrupt arrived, check src */ ++ isr.full = sst_shim_read(drv->shim, SST_ISRX); ++ if (isr.part.done_interrupt) { ++ /* Mask all interrupts till this one is processsed */ ++ set_imr_interrupts(drv, false); ++ /* Clear done bit */ ++ spin_lock(&drv->ipc_spin_lock); ++ header.full = sst_shim_read(drv->shim, drv->ipc_reg.ipcx); ++ header.part.done = 0; ++ sst_shim_write(drv->shim, drv->ipc_reg.ipcx, header.full); ++ /* write 1 to clear status register */; ++ isr.part.done_interrupt = 1; ++ sst_shim_write(drv->shim, SST_ISRX, isr.full); ++ spin_unlock(&drv->ipc_spin_lock); ++ queue_work(drv->post_msg_wq, &sst_drv_ctx->ipc_post_msg.wq); ++ ++ /* Un mask done and busy intr */ ++ set_imr_interrupts(drv, true); ++ retval = IRQ_HANDLED; ++ } ++ if (isr.part.busy_interrupt) { ++ /* Mask all interrupts till we process it in bottom half */ ++ set_imr_interrupts(drv, false); ++ header.full = sst_shim_read(drv->shim, drv->ipc_reg.ipcd); ++ if (sst_create_ipc_msg(&msg, header.part.large)) { ++ pr_err("No memory available\n"); ++ drv->ops->clear_interrupt(); ++ return IRQ_HANDLED; ++ } ++ if (header.part.large) { ++ size = header.part.data; ++ if (SST_VALIDATE_MAILBOX_SIZE(size)) { ++ memcpy_fromio(msg->mailbox_data, ++ drv->mailbox + drv->mailbox_recv_offset + 4, size); ++ } else { ++ pr_err("Mailbox not copied, payload siz is: %u\n", size); ++ header.part.data = 0; ++ } ++ } ++ msg->header = header; ++ msg->is_process_reply = ++ SST_IS_PROCESS_REPLY(msg->header.part.msg_id); ++ spin_lock(&drv->rx_msg_lock); ++ list_add_tail(&msg->node, &drv->rx_list); ++ spin_unlock(&drv->rx_msg_lock); ++ drv->ops->clear_interrupt(); ++ retval = IRQ_WAKE_THREAD; ++ } ++ return retval; ++} ++ ++static int sst_save_dsp_context_v2(struct intel_sst_drv *sst) ++{ ++ unsigned int pvt_id; ++ struct ipc_post *msg = NULL; ++ struct ipc_dsp_hdr dsp_hdr; ++ struct sst_block *block; ++ ++ /*send msg to fw*/ ++ pvt_id = sst_assign_pvt_id(sst); ++ if (sst_create_block_and_ipc_msg(&msg, true, sst, &block, ++ IPC_CMD, pvt_id)) { ++ pr_err("msg/block alloc failed. Not proceeding with context save\n"); ++ return 0; ++ } ++ ++ sst_fill_header_mrfld(&msg->mrfld_header, IPC_CMD, ++ SST_TASK_ID_MEDIA, 1, pvt_id); ++ msg->mrfld_header.p.header_low_payload = sizeof(dsp_hdr); ++ msg->mrfld_header.p.header_high.part.res_rqd = 1; ++ sst_fill_header_dsp(&dsp_hdr, IPC_PREP_D3, PIPE_RSVD, pvt_id); ++ memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr)); ++ ++ sst_add_to_dispatch_list_and_post(sst, msg); ++ /*wait for reply*/ ++ if (sst_wait_timeout(sst, block)) { ++ pr_err("sst: err fw context save timeout ...\n"); ++ pr_err("not suspending FW!!!"); ++ sst_free_block(sst, block); ++ return -EIO; ++ } ++ if (block->ret_code) { ++ pr_err("fw responded w/ error %d", block->ret_code); ++ sst_free_block(sst, block); ++ return -EIO; ++ } ++ ++ sst_free_block(sst, block); ++ return 0; ++} ++ ++static int sst_save_dsp_context(struct intel_sst_drv *sst) ++{ ++ struct snd_sst_ctxt_params fw_context; ++ unsigned int pvt_id; ++ struct ipc_post *msg = NULL; ++ struct sst_block *block; ++ pr_debug("%s: Enter\n", __func__); ++ ++ /*send msg to fw*/ ++ pvt_id = sst_assign_pvt_id(sst_drv_ctx); ++ if (sst_create_block_and_ipc_msg(&msg, true, sst_drv_ctx, &block, ++ IPC_IA_GET_FW_CTXT, pvt_id)) { ++ pr_err("msg/block alloc failed. Not proceeding with context save\n"); ++ return -ENOMEM; ++ } ++ sst_fill_header(&msg->header, IPC_IA_GET_FW_CTXT, 1, pvt_id); ++ msg->header.part.data = sizeof(fw_context) + sizeof(u32); ++ fw_context.address = virt_to_phys((void *)sst_drv_ctx->fw_cntx); ++ fw_context.size = FW_CONTEXT_MEM; ++ memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); ++ memcpy(msg->mailbox_data + sizeof(u32), ++ &fw_context, sizeof(fw_context)); ++ sst_add_to_dispatch_list_and_post(sst, msg); ++ /*wait for reply*/ ++ if (sst_wait_timeout(sst_drv_ctx, block)) ++ pr_err("sst: err fw context save timeout ...\n"); ++ pr_debug("fw context saved ...\n"); ++ if (block->ret_code) ++ sst_drv_ctx->fw_cntx_size = 0; ++ else ++ sst_drv_ctx->fw_cntx_size = *sst_drv_ctx->fw_cntx; ++ pr_debug("fw copied data %x\n", sst_drv_ctx->fw_cntx_size); ++ sst_free_block(sst_drv_ctx, block); ++ return 0; ++} ++ ++static struct intel_sst_ops mrfld_ops = { ++ .interrupt = intel_sst_interrupt_mrfld, ++ .irq_thread = intel_sst_irq_thread_mfld, ++ .clear_interrupt = intel_sst_clear_intr_mrfld, ++ .start = sst_start_mrfld, ++ .reset = intel_sst_reset_dsp_mrfld, ++ .post_message = sst_post_message_mrfld, ++ .sync_post_message = sst_sync_post_message_mrfld, ++ .process_message = sst_process_message_mrfld, ++ .process_reply = sst_process_reply_mrfld, ++ .save_dsp_context = sst_save_dsp_context_v2, ++ .alloc_stream = sst_alloc_stream_mrfld, ++ .post_download = sst_post_download_mrfld, ++ .do_recovery = sst_do_recovery_mrfld, ++}; ++ ++static struct intel_sst_ops mrfld_32_ops = { ++ .interrupt = intel_sst_intr_mfld, ++ .irq_thread = intel_sst_irq_thread_mfld, ++ .clear_interrupt = intel_sst_clear_intr_mfld, ++ .start = sst_start_mrfld, ++ .reset = intel_sst_reset_dsp_mrfld, ++ .post_message = sst_post_message_mfld, ++ .sync_post_message = sst_sync_post_message_mfld, ++ .process_message = sst_process_message_mfld, ++ .process_reply = sst_process_reply_mfld, ++ .save_dsp_context = sst_save_dsp_context, ++ .restore_dsp_context = sst_restore_fw_context, ++ .alloc_stream = sst_alloc_stream_ctp, ++ .post_download = sst_post_download_byt, ++ .do_recovery = sst_do_recovery, ++}; ++ ++static struct intel_sst_ops ctp_ops = { ++ .interrupt = intel_sst_intr_mfld, ++ .irq_thread = intel_sst_irq_thread_mfld, ++ .clear_interrupt = intel_sst_clear_intr_mfld, ++ .start = sst_start_mfld, ++ .reset = intel_sst_reset_dsp_mfld, ++ .post_message = sst_post_message_mfld, ++ .sync_post_message = sst_sync_post_message_mfld, ++ .process_message = sst_process_message_mfld, ++ .process_reply = sst_process_reply_mfld, ++ .set_bypass = intel_sst_set_bypass_mfld, ++ .save_dsp_context = sst_save_dsp_context, ++ .restore_dsp_context = sst_restore_fw_context, ++ .alloc_stream = sst_alloc_stream_ctp, ++ .post_download = sst_post_download_ctp, ++ .do_recovery = sst_do_recovery, ++}; ++ ++int sst_driver_ops(struct intel_sst_drv *sst) ++{ ++ ++ switch (sst->pci_id) { ++ case SST_MRFLD_PCI_ID: ++ sst->tstamp = SST_TIME_STAMP_MRFLD; ++ sst->ops = &mrfld_ops; ++ return 0; ++ case SST_BYT_PCI_ID: ++ sst->tstamp = SST_TIME_STAMP_BYT; ++ sst->ops = &mrfld_32_ops; ++ return 0; ++ case SST_CLV_PCI_ID: ++ sst->tstamp = SST_TIME_STAMP; ++ sst->ops = &ctp_ops; ++ return 0; ++ default: ++ pr_err("SST Driver capablities missing for pci_id: %x", sst->pci_id); ++ return -EINVAL; ++ }; ++} ++ ++int sst_alloc_drv_context(struct device *dev) ++{ ++ struct intel_sst_drv *ctx; ++ mutex_lock(&drv_ctx_lock); ++ if (sst_drv_ctx) { ++ pr_err("Only one sst handle is supported\n"); ++ mutex_unlock(&drv_ctx_lock); ++ return -EBUSY; ++ } ++ pr_debug("%s: %d", __func__, __LINE__); ++ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); ++ if (!ctx) { ++ pr_err("malloc fail\n"); ++ mutex_unlock(&drv_ctx_lock); ++ return -ENOMEM; ++ } ++ sst_drv_ctx = ctx; ++ mutex_unlock(&drv_ctx_lock); ++ return 0; ++} ++ ++static ssize_t sst_sysfs_get_recovery(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct intel_sst_drv *ctx = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%d\n", ctx->sst_state); ++} ++ ++ ++static ssize_t sst_sysfs_set_recovery(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t len) ++{ ++ long val; ++ struct intel_sst_drv *ctx = dev_get_drvdata(dev); ++ ++ if (kstrtol(buf, 0, &val)) ++ return -EINVAL; ++ ++ if (val == 1) { ++ if (!atomic_read(&ctx->pm_usage_count)) { ++ pr_debug("%s: set sst state to uninit...\n", __func__); ++ sst_set_fw_state_locked(ctx, SST_UN_INIT); ++ } else { ++ pr_err("%s: not setting sst state... %d\n", __func__, ++ atomic_read(&ctx->pm_usage_count)); ++ pr_err("Unrecoverable state....\n"); ++ BUG(); ++ return -EPERM; ++ } ++ } ++ ++ return len; ++} ++ ++static DEVICE_ATTR(audio_recovery, S_IRUGO | S_IWUSR, ++ sst_sysfs_get_recovery, sst_sysfs_set_recovery); ++ ++int sst_request_firmware_async(struct intel_sst_drv *ctx) ++{ ++ int ret = 0; ++ ++ snprintf(ctx->firmware_name, sizeof(ctx->firmware_name), ++ "%s%04x%s", "fw_sst_", ++ ctx->pci_id, ".bin"); ++ pr_debug("Requesting FW %s now...\n", ctx->firmware_name); ++ ++ trace_sst_fw_download("Request firmware async", ctx->sst_state); ++ ++ ret = request_firmware_nowait(THIS_MODULE, 1, ctx->firmware_name, ++ ctx->dev, GFP_KERNEL, ctx, sst_firmware_load_cb); ++ if (ret) ++ pr_err("could not load firmware %s error %d\n", ctx->firmware_name, ret); ++ ++ return ret; ++} ++/* ++* intel_sst_probe - PCI probe function ++* ++* @pci: PCI device structure ++* @pci_id: PCI device ID structure ++* ++* This function is called by OS when a device is found ++* This enables the device, interrupt etc ++*/ ++static int intel_sst_probe(struct pci_dev *pci, ++ const struct pci_device_id *pci_id) ++{ ++ int i, ret = 0; ++ struct intel_sst_ops *ops; ++ struct sst_platform_info *sst_pdata = pci->dev.platform_data; ++ int ddr_base; ++ u32 ssp_base_add; ++ u32 dma_base_add; ++ u32 len; ++ ++ ++ ++ pr_debug("Probe for DID %x\n", pci->device); ++ ret = sst_alloc_drv_context(&pci->dev); ++ if (ret) ++ return ret; ++ ++ sst_drv_ctx->dev = &pci->dev; ++ sst_drv_ctx->pci_id = pci->device; ++ if (!sst_pdata) ++ return -EINVAL; ++ sst_drv_ctx->pdata = sst_pdata; ++ ++ if (!sst_drv_ctx->pdata->probe_data) ++ return -EINVAL; ++ memcpy(&sst_drv_ctx->info, sst_drv_ctx->pdata->probe_data, ++ sizeof(sst_drv_ctx->info)); ++ ++ sst_drv_ctx->use_32bit_ops = sst_drv_ctx->pdata->ipc_info->use_32bit_ops; ++ sst_drv_ctx->mailbox_recv_offset = sst_drv_ctx->pdata->ipc_info->mbox_recv_off; ++ ++ if (0 != sst_driver_ops(sst_drv_ctx)) ++ return -EINVAL; ++ ops = sst_drv_ctx->ops; ++ mutex_init(&sst_drv_ctx->stream_lock); ++ mutex_init(&sst_drv_ctx->sst_lock); ++ mutex_init(&sst_drv_ctx->mixer_ctrl_lock); ++ mutex_init(&sst_drv_ctx->csr_lock); ++ ++ sst_drv_ctx->stream_cnt = 0; ++ sst_drv_ctx->fw_in_mem = NULL; ++ sst_drv_ctx->vcache.file1_in_mem = NULL; ++ sst_drv_ctx->vcache.file2_in_mem = NULL; ++ sst_drv_ctx->vcache.size1 = 0; ++ sst_drv_ctx->vcache.size2 = 0; ++ ++ /* we don't use dma, so set to 0*/ ++ sst_drv_ctx->use_dma = 0; //1; ++ sst_drv_ctx->use_lli = 1; ++ ++ INIT_LIST_HEAD(&sst_drv_ctx->memcpy_list); ++ INIT_LIST_HEAD(&sst_drv_ctx->libmemcpy_list); ++ ++ INIT_LIST_HEAD(&sst_drv_ctx->ipc_dispatch_list); ++ INIT_LIST_HEAD(&sst_drv_ctx->block_list); ++ INIT_LIST_HEAD(&sst_drv_ctx->rx_list); ++ INIT_WORK(&sst_drv_ctx->ipc_post_msg.wq, ops->post_message); ++ init_waitqueue_head(&sst_drv_ctx->wait_queue); ++ ++ sst_drv_ctx->mad_wq = create_singlethread_workqueue("sst_mad_wq"); ++ if (!sst_drv_ctx->mad_wq) ++ goto do_free_drv_ctx; ++ sst_drv_ctx->post_msg_wq = ++ create_singlethread_workqueue("sst_post_msg_wq"); ++ if (!sst_drv_ctx->post_msg_wq) ++ goto free_mad_wq; ++ ++ spin_lock_init(&sst_drv_ctx->ipc_spin_lock); ++ spin_lock_init(&sst_drv_ctx->block_lock); ++ spin_lock_init(&sst_drv_ctx->pvt_id_lock); ++ spin_lock_init(&sst_drv_ctx->rx_msg_lock); ++ ++ sst_drv_ctx->ipc_reg.ipcx = SST_IPCX + sst_drv_ctx->pdata->ipc_info->ipc_offset; ++ sst_drv_ctx->ipc_reg.ipcd = SST_IPCD + sst_drv_ctx->pdata->ipc_info->ipc_offset; ++ pr_debug("ipcx 0x%x ipxd 0x%x", sst_drv_ctx->ipc_reg.ipcx, ++ sst_drv_ctx->ipc_reg.ipcd); ++ ++ pr_info("Got drv data max stream %d\n", ++ sst_drv_ctx->info.max_streams); ++ for (i = 1; i <= sst_drv_ctx->info.max_streams; i++) { ++ struct stream_info *stream = &sst_drv_ctx->streams[i]; ++ memset(stream, 0, sizeof(*stream)); ++ stream->pipe_id = PIPE_RSVD; ++ mutex_init(&stream->lock); ++ } ++ ++ ret = sst_request_firmware_async(sst_drv_ctx); ++ if (ret) { ++ pr_err("Firmware download failed:%d\n", ret); ++ goto do_free_mem; ++ } ++ /* Init the device */ ++ ret = pci_enable_device(pci); ++ if (ret) { ++ pr_err("device can't be enabled\n"); ++ goto do_free_mem; ++ } ++ sst_drv_ctx->pci = pci_dev_get(pci); ++ ret = pci_request_regions(pci, SST_DRV_NAME); ++ if (ret) ++ goto do_disable_device; ++ /* map registers */ ++ /* SST Shim */ ++ ++ if (sst_drv_ctx->pci_id == SST_MRFLD_PCI_ID) { ++ sst_drv_ctx->ddr_base = pci_resource_start(pci, 0); ++ /* ++ * check that the relocated IMR base matches with FW Binary ++ * put temporary check till better soln is available for FW ++ */ ++ ddr_base = relocate_imr_addr_mrfld(sst_drv_ctx->ddr_base); ++ if (!sst_drv_ctx->pdata->lib_info) { ++ pr_err("%s:lib_info pointer NULL\n", __func__); ++ ret = -EINVAL; ++ goto do_release_regions; ++ } ++ if (ddr_base != sst_drv_ctx->pdata->lib_info->mod_base) { ++ pr_err("FW LSP DDR BASE does not match with IFWI\n"); ++ ret = -EINVAL; ++ goto do_release_regions; ++ } ++ sst_drv_ctx->ddr_end = pci_resource_end(pci, 0); ++ ++ sst_drv_ctx->ddr = pci_ioremap_bar(pci, 0); ++ if (!sst_drv_ctx->ddr) ++ goto do_unmap_ddr; ++ pr_debug("sst: DDR Ptr %p\n", sst_drv_ctx->ddr); ++ } else { ++ sst_drv_ctx->ddr = NULL; ++ } ++ ++ /* SHIM */ ++ sst_drv_ctx->shim_phy_add = pci_resource_start(pci, 1); ++ sst_drv_ctx->shim = pci_ioremap_bar(pci, 1); ++ if (!sst_drv_ctx->shim) ++ goto do_release_regions; ++ pr_debug("SST Shim Ptr %p\n", sst_drv_ctx->shim); ++ ++ /* Shared SRAM */ ++ sst_drv_ctx->mailbox_add = pci_resource_start(pci, 2); ++ sst_drv_ctx->mailbox = pci_ioremap_bar(pci, 2); ++ if (!sst_drv_ctx->mailbox) ++ goto do_unmap_shim; ++ pr_debug("SRAM Ptr %p\n", sst_drv_ctx->mailbox); ++ ++ /* IRAM */ ++ sst_drv_ctx->iram_end = pci_resource_end(pci, 3); ++ sst_drv_ctx->iram_base = pci_resource_start(pci, 3); ++ sst_drv_ctx->iram = pci_ioremap_bar(pci, 3); ++ if (!sst_drv_ctx->iram) ++ goto do_unmap_sram; ++ pr_debug("IRAM Ptr %p\n", sst_drv_ctx->iram); ++ ++ /* DRAM */ ++ sst_drv_ctx->dram_end = pci_resource_end(pci, 4); ++ sst_drv_ctx->dram_base = pci_resource_start(pci, 4); ++ sst_drv_ctx->dram = pci_ioremap_bar(pci, 4); ++ if (!sst_drv_ctx->dram) ++ goto do_unmap_iram; ++ pr_debug("DRAM Ptr %p\n", sst_drv_ctx->dram); ++ ++ if ((sst_pdata->pdata != NULL) && ++ (sst_pdata->debugfs_data != NULL)) { ++ if (sst_pdata->ssp_data != NULL) { ++ /* SSP Register */ ++ ssp_base_add = sst_pdata->ssp_data->base_add; ++ len = sst_pdata->debugfs_data->ssp_reg_size; ++ for (i = 0; i < sst_pdata->debugfs_data->num_ssp; i++) { ++ sst_drv_ctx->debugfs.ssp[i] = ++ devm_ioremap(&pci->dev, ++ ssp_base_add + (len * i), len); ++ if (!sst_drv_ctx->debugfs.ssp[i]) { ++ pr_warn("ssp ioremap failed\n"); ++ continue; ++ } ++ ++ pr_debug("\n ssp io 0x%p ssp 0x%x size 0x%x", ++ sst_drv_ctx->debugfs.ssp[i], ++ ssp_base_add, len); ++ } ++ } ++ ++ /* DMA Register */ ++ dma_base_add = sst_pdata->pdata->sst_dma_base[0]; ++ len = sst_pdata->debugfs_data->dma_reg_size; ++ for (i = 0; i < sst_pdata->debugfs_data->num_dma; i++) { ++ sst_drv_ctx->debugfs.dma_reg[i] = ++ devm_ioremap(&pci->dev, ++ dma_base_add + (len * i), len); ++ if (!sst_drv_ctx->debugfs.dma_reg[i]) { ++ pr_warn("dma ioremap failed\n"); ++ continue; ++ } ++ ++ pr_debug("\n dma io 0x%p ssp 0x%x size 0x%x", ++ sst_drv_ctx->debugfs.dma_reg[i], ++ dma_base_add, len); ++ } ++ } ++ ++ /* Do not access iram/dram etc before LPE is reset */ ++ ++ sst_drv_ctx->dump_buf.iram_buf.size = pci_resource_len(pci, 3); ++ sst_drv_ctx->dump_buf.iram_buf.buf = kzalloc(sst_drv_ctx->dump_buf.iram_buf.size, ++ GFP_KERNEL); ++ if (!sst_drv_ctx->dump_buf.iram_buf.buf) { ++ pr_err("%s: no memory\n", __func__); ++ ret = -ENOMEM; ++ goto do_unmap_dram; ++ } ++ ++ sst_drv_ctx->dump_buf.dram_buf.size = pci_resource_len(pci, 4); ++ sst_drv_ctx->dump_buf.dram_buf.buf = kzalloc(sst_drv_ctx->dump_buf.dram_buf.size, ++ GFP_KERNEL); ++ if (!sst_drv_ctx->dump_buf.dram_buf.buf) { ++ pr_err("%s: no memory\n", __func__); ++ ret = -ENOMEM; ++ goto do_free_iram_buf; ++ } ++ ++ pr_debug("\niram len 0x%x dram len 0x%x", ++ sst_drv_ctx->dump_buf.iram_buf.size, ++ sst_drv_ctx->dump_buf.dram_buf.size); ++ ++ if (sst_drv_ctx->pci_id == SST_CLV_PCI_ID) { ++ sst_drv_ctx->probe_bytes = kzalloc(SST_MAX_BIN_BYTES, GFP_KERNEL); ++ if (!sst_drv_ctx->probe_bytes) { ++ pr_err("%s: no memory\n", __func__); ++ ret = -ENOMEM; ++ goto do_free_dram_buf; ++ } ++ } ++ ++ sst_set_fw_state_locked(sst_drv_ctx, SST_UN_INIT); ++ sst_drv_ctx->irq_num = pci->irq; ++ /* Register the ISR */ ++ ret = request_threaded_irq(pci->irq, sst_drv_ctx->ops->interrupt, ++ sst_drv_ctx->ops->irq_thread, 0, SST_DRV_NAME, ++ sst_drv_ctx); ++ if (ret) ++ goto do_free_probe_bytes; ++ pr_debug("Registered IRQ 0x%x\n", pci->irq); ++ ++ /*Register LPE Control as misc driver*/ ++ ret = misc_register(&lpe_ctrl); ++ if (ret) { ++ pr_err("couldn't register control device\n"); ++ goto do_free_irq; ++ } ++ /* default intr are unmasked so set this as masked */ ++ if (sst_drv_ctx->pci_id == SST_MRFLD_PCI_ID) ++ sst_shim_write64(sst_drv_ctx->shim, SST_IMRX, 0xFFFF0038); ++ ++ if (sst_drv_ctx->use_32bit_ops) { ++ pr_debug("allocate mem for context save/restore\n "); ++ /*allocate mem for fw context save during suspend*/ ++ sst_drv_ctx->fw_cntx = kzalloc(FW_CONTEXT_MEM, GFP_KERNEL); ++ if (!sst_drv_ctx->fw_cntx) { ++ ret = -ENOMEM; ++ goto do_free_misc; ++ } ++ /*setting zero as that is valid mem to restore*/ ++ sst_drv_ctx->fw_cntx_size = 0; ++ } ++ if (sst_drv_ctx->pci_id == SST_CLV_PCI_ID) { ++ u32 csr; ++ u32 csr2; ++ u32 clkctl; ++ ++ /*set lpe start clock and ram size*/ ++ csr = sst_shim_read(sst_drv_ctx->shim, SST_CSR); ++ csr |= 0x30000; ++ /*make sure clksel set to OSC for SSP0,1 (default)*/ ++ csr &= 0xFFFFFFF3; ++ sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr); ++ ++ /*set clock output enable for SSP0,1,3*/ ++ clkctl = sst_shim_read(sst_drv_ctx->shim, SST_CLKCTL); ++ if (sst_drv_ctx->pci_id == SST_CLV_PCI_ID) ++ clkctl |= (0x7 << 16); ++ else ++ clkctl |= ((1<<16)|(1<<17)); ++ sst_shim_write(sst_drv_ctx->shim, SST_CLKCTL, clkctl); ++ ++ /* set SSP0 & SSP1 disable DMA Finish*/ ++ csr2 = sst_shim_read(sst_drv_ctx->shim, SST_CSR2); ++ /*set SSP3 disable DMA finsh for SSSP3 */ ++ csr2 |= BIT(1)|BIT(2); ++ sst_shim_write(sst_drv_ctx->shim, SST_CSR2, csr2); ++ } ++ if (sst_drv_ctx->pdata->ssp_data) { ++ if (sst_drv_ctx->pdata->ssp_data->gpio_in_use) ++ sst_set_gpio_conf(&sst_drv_ctx->pdata->ssp_data->gpio); ++ } ++ pci_set_drvdata(pci, sst_drv_ctx); ++ pm_runtime_allow(sst_drv_ctx->dev); ++ pm_runtime_put_noidle(sst_drv_ctx->dev); ++ register_sst(sst_drv_ctx->dev); ++ sst_debugfs_init(sst_drv_ctx); ++ sst_drv_ctx->qos = kzalloc(sizeof(struct pm_qos_request), ++ GFP_KERNEL); ++ if (!sst_drv_ctx->qos) ++ goto do_free_misc; ++ pm_qos_add_request(sst_drv_ctx->qos, PM_QOS_CPU_DMA_LATENCY, ++ PM_QOS_DEFAULT_VALUE); ++ ++ ret = device_create_file(sst_drv_ctx->dev, &dev_attr_audio_recovery); ++ if (ret) { ++ pr_err("could not create sysfs %s file\n", ++ dev_attr_audio_recovery.attr.name); ++ goto do_free_qos; ++ } ++ ++ pr_info("%s successfully done!\n", __func__); ++ return ret; ++ ++do_free_qos: ++ pm_qos_remove_request(sst_drv_ctx->qos); ++ kfree(sst_drv_ctx->qos); ++do_free_misc: ++ misc_deregister(&lpe_ctrl); ++do_free_irq: ++ free_irq(pci->irq, sst_drv_ctx); ++do_free_probe_bytes: ++ if (sst_drv_ctx->pci_id == SST_CLV_PCI_ID) ++ kfree(sst_drv_ctx->probe_bytes); ++do_free_dram_buf: ++#ifdef CONFIG_DEBUG_FS ++ if (sst_drv_ctx->pci_id == SST_CLV_PCI_ID) ++ kfree(sst_drv_ctx->dump_buf.dram_buf.buf); ++do_free_iram_buf: ++ if (sst_drv_ctx->pci_id == SST_CLV_PCI_ID) ++ kfree(sst_drv_ctx->dump_buf.iram_buf.buf); ++#endif ++do_unmap_dram: ++ iounmap(sst_drv_ctx->dram); ++do_unmap_iram: ++ iounmap(sst_drv_ctx->iram); ++do_unmap_sram: ++ iounmap(sst_drv_ctx->mailbox); ++do_unmap_shim: ++ iounmap(sst_drv_ctx->shim); ++ ++do_unmap_ddr: ++ if (sst_drv_ctx->ddr) ++ iounmap(sst_drv_ctx->ddr); ++ ++do_release_regions: ++ pci_release_regions(pci); ++do_disable_device: ++ pci_disable_device(pci); ++do_free_mem: ++ destroy_workqueue(sst_drv_ctx->post_msg_wq); ++free_mad_wq: ++ destroy_workqueue(sst_drv_ctx->mad_wq); ++do_free_drv_ctx: ++ sst_drv_ctx = NULL; ++ pr_err("Probe failed with %d\n", ret); ++ return ret; ++} ++ ++/** ++* intel_sst_remove - PCI remove function ++* ++* @pci: PCI device structure ++* ++* This function is called by OS when a device is unloaded ++* This frees the interrupt etc ++*/ ++static void intel_sst_remove(struct pci_dev *pci) ++{ ++ struct intel_sst_drv *sst_drv_ctx = pci_get_drvdata(pci); ++ sst_debugfs_exit(sst_drv_ctx); ++ pm_runtime_get_noresume(sst_drv_ctx->dev); ++ pm_runtime_forbid(sst_drv_ctx->dev); ++ unregister_sst(sst_drv_ctx->dev); ++ pci_dev_put(sst_drv_ctx->pci); ++ sst_set_fw_state_locked(sst_drv_ctx, SST_UN_INIT); ++ misc_deregister(&lpe_ctrl); ++ free_irq(pci->irq, sst_drv_ctx); ++ ++ iounmap(sst_drv_ctx->dram); ++ iounmap(sst_drv_ctx->iram); ++ iounmap(sst_drv_ctx->mailbox); ++ iounmap(sst_drv_ctx->shim); ++#ifdef CONFIG_DEBUG_FS ++ if (sst_drv_ctx->pci_id == SST_CLV_PCI_ID) { ++ kfree(sst_drv_ctx->dump_buf.iram_buf.buf); ++ kfree(sst_drv_ctx->dump_buf.dram_buf.buf); ++ } ++#endif ++ if (sst_drv_ctx->pci_id == SST_CLV_PCI_ID) ++ kfree(sst_drv_ctx->probe_bytes); ++ ++ device_remove_file(sst_drv_ctx->dev, &dev_attr_audio_recovery); ++ kfree(sst_drv_ctx->fw_cntx); ++ kfree(sst_drv_ctx->runtime_param.param.addr); ++ flush_scheduled_work(); ++ destroy_workqueue(sst_drv_ctx->post_msg_wq); ++ destroy_workqueue(sst_drv_ctx->mad_wq); ++ pm_qos_remove_request(sst_drv_ctx->qos); ++ kfree(sst_drv_ctx->qos); ++ kfree(sst_drv_ctx->fw_sg_list.src); ++ kfree(sst_drv_ctx->fw_sg_list.dst); ++ sst_drv_ctx->fw_sg_list.list_len = 0; ++ kfree(sst_drv_ctx->fw_in_mem); ++ sst_drv_ctx->fw_in_mem = NULL; ++ sst_memcpy_free_resources(); ++ sst_drv_ctx = NULL; ++ pci_release_regions(pci); ++ pci_disable_device(pci); ++ pci_set_drvdata(pci, NULL); ++} ++ ++inline void sst_save_shim64(struct intel_sst_drv *ctx, ++ void __iomem *shim, ++ struct sst_shim_regs64 *shim_regs) ++{ ++ unsigned long irq_flags; ++ spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags); ++ ++ shim_regs->csr = sst_shim_read64(shim, SST_CSR), ++ shim_regs->pisr = sst_shim_read64(shim, SST_PISR), ++ shim_regs->pimr = sst_shim_read64(shim, SST_PIMR), ++ shim_regs->isrx = sst_shim_read64(shim, SST_ISRX), ++ shim_regs->isrd = sst_shim_read64(shim, SST_ISRD), ++ shim_regs->imrx = sst_shim_read64(shim, SST_IMRX), ++ shim_regs->imrd = sst_shim_read64(shim, SST_IMRD), ++ shim_regs->ipcx = sst_shim_read64(shim, ctx->ipc_reg.ipcx), ++ shim_regs->ipcd = sst_shim_read64(shim, ctx->ipc_reg.ipcd), ++ shim_regs->isrsc = sst_shim_read64(shim, SST_ISRSC), ++ shim_regs->isrlpesc = sst_shim_read64(shim, SST_ISRLPESC), ++ shim_regs->imrsc = sst_shim_read64(shim, SST_IMRSC), ++ shim_regs->imrlpesc = sst_shim_read64(shim, SST_IMRLPESC), ++ shim_regs->ipcsc = sst_shim_read64(shim, SST_IPCSC), ++ shim_regs->ipclpesc = sst_shim_read64(shim, SST_IPCLPESC), ++ shim_regs->clkctl = sst_shim_read64(shim, SST_CLKCTL), ++ shim_regs->csr2 = sst_shim_read64(shim, SST_CSR2); ++ ++ spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags); ++} ++ ++static inline void sst_restore_shim64(struct intel_sst_drv *ctx, ++ void __iomem *shim, ++ struct sst_shim_regs64 *shim_regs) ++{ ++ unsigned long irq_flags; ++ spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags); ++ sst_shim_write64(shim, SST_IMRX, shim_regs->imrx), ++ spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags); ++} ++ ++/* ++ * The runtime_suspend/resume is pretty much similar to the legacy ++ * suspend/resume with the noted exception below: The PCI core takes care of ++ * taking the system through D3hot and restoring it back to D0 and so there is ++ * no need to duplicate that here. ++ */ ++static int intel_sst_runtime_suspend(struct device *dev) ++{ ++ union config_status_reg csr; ++ int ret = 0; ++ struct intel_sst_drv *ctx = dev_get_drvdata(dev); ++ ++ pr_info("runtime_suspend called\n"); ++ if (ctx->sst_state == SST_UN_INIT) { ++ pr_debug("LPE is already in UNINIT state, No action"); ++ return 0; ++ } ++ /*save fw context*/ ++ if (ctx->ops->save_dsp_context(ctx)) ++ return -EBUSY; ++ ++ if (ctx->pci_id == SST_CLV_PCI_ID) { ++ /*Assert RESET on LPE Processor*/ ++ csr.full = sst_shim_read(ctx->shim, SST_CSR); ++ ctx->csr_value = csr.full; ++ csr.full = csr.full | 0x2; ++ sst_shim_write(ctx->shim, SST_CSR, csr.full); ++ } ++ ++ /* Move the SST state to Suspended */ ++ sst_set_fw_state_locked(ctx, SST_SUSPENDED); ++ ++ flush_workqueue(ctx->post_msg_wq); ++ synchronize_irq(ctx->irq_num); ++ ++ if (ctx->pci_id == SST_BYT_PCI_ID || ctx->pci_id == SST_CHT_PCI_ID) { ++ /* save the shim registers because PMC doesn't save state */ ++ sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64); ++ } ++ return ret; ++} ++ ++static int intel_sst_runtime_resume(struct device *dev) ++{ ++ u32 csr; ++ int ret = 0; ++ struct intel_sst_drv *ctx = dev_get_drvdata(dev); ++ ++ pr_info("runtime_resume called\n"); ++ ++ if (ctx->pci_id == SST_BYT_PCI_ID || ctx->pci_id == SST_CHT_PCI_ID) { ++ /* wait for device power up a/c to PCI spec */ ++ usleep_range(10000, 11000); ++ sst_restore_shim64(ctx, ctx->shim, ctx->shim_regs64); ++ } ++ ++ if (ctx->pci_id == SST_CLV_PCI_ID) { ++ csr = sst_shim_read(ctx->shim, SST_CSR); ++ /* ++ * To restore the csr_value after S0ix and S3 states. ++ * The value 0x30000 is to enable LPE dram high and low addresses. ++ * Reference: ++ * Penwell Audio Voice Module HAS 1.61 Section - 13.12.1 - ++ * CSR - Configuration and Status Register. ++ */ ++ csr |= (ctx->csr_value | 0x30000); ++ sst_shim_write(ctx->shim, SST_CSR, csr); ++ if (sst_drv_ctx->pdata->ssp_data) { ++ if (ctx->pdata->ssp_data->gpio_in_use) ++ sst_set_gpio_conf(&ctx->pdata->ssp_data->gpio); ++ } ++ } ++ /* When fw_clear_cache is set, clear the cached firmware copy */ ++ /* fw_clear_cache is set through debugfs support */ ++ if (atomic_read(&ctx->fw_clear_cache) && ctx->fw_in_mem) { ++ pr_debug("Clearing the cached firmware\n"); ++ kfree(ctx->fw_in_mem); ++ ctx->fw_in_mem = NULL; ++ atomic_set(&ctx->fw_clear_cache, 0); ++ } ++ ++ sst_set_fw_state_locked(ctx, SST_UN_INIT); ++ return ret; ++} ++ ++static int intel_sst_suspend(struct device *dev) ++{ ++ int retval = 0, usage_count; ++ struct intel_sst_drv *ctx = dev_get_drvdata(dev); ++ ++ usage_count = atomic_read(&ctx->pm_usage_count); ++ if (usage_count) { ++ pr_err("Ret error for suspend:%d\n", usage_count); ++ return -EBUSY; ++ } ++ retval = intel_sst_runtime_suspend(dev); ++ ++ return retval; ++} ++ ++static int intel_sst_runtime_idle(struct device *dev) ++{ ++ struct intel_sst_drv *ctx = dev_get_drvdata(dev); ++ ++ pr_info("runtime_idle called\n"); ++ if (ctx->sst_state != SST_UN_INIT) { ++ pm_schedule_suspend(dev, SST_SUSPEND_DELAY); ++ return -EBUSY; ++ } else { ++ return 0; ++ } ++ return -EBUSY; ++ ++} ++ ++static void sst_do_shutdown(struct intel_sst_drv *ctx) ++{ ++ int retval = 0; ++ unsigned int pvt_id; ++ struct ipc_post *msg = NULL; ++ struct sst_block *block = NULL; ++ ++ pr_debug(" %s called\n", __func__); ++ if (ctx->sst_state == SST_SUSPENDED || ++ ctx->sst_state == SST_UN_INIT) { ++ sst_set_fw_state_locked(ctx, SST_SHUTDOWN); ++ pr_debug("sst is already in suspended/un-int state\n"); ++ return; ++ } ++ if (!ctx->use_32bit_ops) ++ return; ++ ++ sst_set_fw_state_locked(ctx, SST_SHUTDOWN); ++ flush_workqueue(ctx->post_msg_wq); ++ pvt_id = sst_assign_pvt_id(ctx); ++ retval = sst_create_block_and_ipc_msg(&msg, false, ++ ctx, &block, ++ IPC_IA_PREPARE_SHUTDOWN, pvt_id); ++ if (retval) { ++ pr_err("sst_create_block returned error!\n"); ++ return; ++ } ++ sst_fill_header(&msg->header, IPC_IA_PREPARE_SHUTDOWN, 0, pvt_id); ++ sst_add_to_dispatch_list_and_post(ctx, msg); ++ sst_wait_timeout(ctx, block); ++ sst_free_block(ctx, block); ++} ++ ++ ++/** ++* sst_pci_shutdown - PCI shutdown function ++* ++* @pci: PCI device structure ++* ++* This function is called by OS when a device is shutdown/reboot ++* ++*/ ++ ++static void sst_pci_shutdown(struct pci_dev *pci) ++{ ++ struct intel_sst_drv *ctx = pci_get_drvdata(pci); ++ ++ pr_debug(" %s called\n", __func__); ++ ++ sst_do_shutdown(ctx); ++ disable_irq_nosync(pci->irq); ++} ++ ++/** ++* sst_acpi_shutdown - platform shutdown function ++* ++* @pci: Platform device structure ++* ++* This function is called by OS when a device is shutdown/reboot ++* ++*/ ++static void sst_acpi_shutdown(struct platform_device *pdev) ++{ ++ struct intel_sst_drv *ctx = platform_get_drvdata(pdev); ++ int irq = platform_get_irq(pdev, 0); ++ ++ pr_debug(" %s called\n", __func__); ++ ++ sst_do_shutdown(ctx); ++ disable_irq_nosync(irq); ++} ++ ++static const struct dev_pm_ops intel_sst_pm = { ++ .suspend = intel_sst_suspend, ++ .resume = intel_sst_runtime_resume, ++ .runtime_suspend = intel_sst_runtime_suspend, ++ .runtime_resume = intel_sst_runtime_resume, ++ .runtime_idle = intel_sst_runtime_idle, ++}; ++ ++static const struct acpi_device_id sst_acpi_ids[]; ++ ++struct sst_platform_info *sst_get_acpi_driver_data(const char *hid) ++{ ++ const struct acpi_device_id *id; ++ ++ pr_debug("%s", __func__); ++ for (id = sst_acpi_ids; id->id[0]; id++) ++ if (!strncmp(id->id, hid, 16)) ++ return (struct sst_platform_info *)id->driver_data; ++ return NULL; ++} ++ ++/* PCI Routines */ ++static DEFINE_PCI_DEVICE_TABLE(intel_sst_ids) = { ++ { PCI_VDEVICE(INTEL, SST_CLV_PCI_ID), 0}, ++ { PCI_VDEVICE(INTEL, SST_MRFLD_PCI_ID), 0}, ++ { 0, } ++}; ++MODULE_DEVICE_TABLE(pci, intel_sst_ids); ++ ++static const struct acpi_device_id sst_acpi_ids[] = { ++ { "LPE0F28", (kernel_ulong_t) &byt_rvp_platform_data }, ++ { "LPE0F281", (kernel_ulong_t) &byt_ffrd8_platform_data }, ++ { "80860F28", (kernel_ulong_t) &byt_ffrd8_platform_data }, ++ { "808622A8", (kernel_ulong_t) &cht_platform_data }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(acpi, sst_acpi_ids); ++ ++static struct pci_driver driver = { ++ .name = SST_DRV_NAME, ++ .id_table = intel_sst_ids, ++ .probe = intel_sst_probe, ++ .remove = intel_sst_remove, ++ .shutdown = sst_pci_shutdown, ++#ifdef CONFIG_PM ++ .driver = { ++ .pm = &intel_sst_pm, ++ }, ++#endif ++}; ++ ++static struct platform_driver sst_acpi_driver = { ++ .driver = { ++ .name = "intel_sst_acpi", ++ .owner = THIS_MODULE, ++ .acpi_match_table = ACPI_PTR(sst_acpi_ids), ++ .pm = &intel_sst_pm, ++ }, ++ .probe = sst_acpi_probe, ++ .remove = sst_acpi_remove, ++ .shutdown = sst_acpi_shutdown, ++}; ++ ++ ++/** ++* intel_sst_init - Module init function ++* ++* Registers with PCI ++* Registers with /dev ++* Init all data strutures ++*/ ++static int __init intel_sst_init(void) ++{ ++ /* Init all variables, data structure etc....*/ ++ int ret = 0; ++ pr_info("INFO: ******** SST DRIVER loading.. Ver: %s\n", ++ SST_DRIVER_VERSION); ++ ++ mutex_init(&drv_ctx_lock); ++ /* Register with PCI */ ++ ret = pci_register_driver(&driver); ++ if (ret) ++ pr_err("PCI register failed\n"); ++ ++ ret = platform_driver_register(&sst_acpi_driver); ++ if (ret) ++ pr_err("ACPI register failed\n"); ++ return ret; ++} ++ ++/** ++* intel_sst_exit - Module exit function ++* ++* Unregisters with PCI ++* Unregisters with /dev ++* Frees all data strutures ++*/ ++static void __exit intel_sst_exit(void) ++{ ++ pci_unregister_driver(&driver); ++ platform_driver_unregister(&sst_acpi_driver); ++ ++ pr_debug("driver unloaded\n"); ++ sst_drv_ctx = NULL; ++ return; ++} ++ ++module_init(intel_sst_init); ++module_exit(intel_sst_exit); +diff --git a/sound/soc/intel/sst/sst.h b/sound/soc/intel/sst/sst.h +new file mode 100644 +index 0000000..4ef3efa +--- /dev/null ++++ b/sound/soc/intel/sst/sst.h +@@ -0,0 +1,932 @@ ++/* ++ * sst.h - Intel SST Driver for audio engine ++ * ++ * Copyright (C) 2008-10 Intel Corporation ++ * Authors: Vinod Koul ++ * Harsha Priya ++ * Dharageswari R ++ * KP Jeeja ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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. ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * Common private declarations for SST ++ */ ++#ifndef __SST_H__ ++#define __SST_H__ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define SST_DRIVER_VERSION "3.0.8" ++ ++/* driver names */ ++#define SST_DRV_NAME "intel_sst_driver" ++#define SST_CLV_PCI_ID 0x08E7 ++#define SST_MRFLD_PCI_ID 0x119A ++#define SST_BYT_PCI_ID 0x0F28 ++#define SST_CHT_PCI_ID 0x22A8 ++ ++#define SST_SUSPEND_DELAY 2000 ++#define FW_CONTEXT_MEM (64*1024) ++#define SST_ICCM_BOUNDARY 4 ++#define SST_CONFIG_SSP_SIGN 0x7ffe8001 ++ ++/* FIXME: All this info should come from platform data ++ * move this when the base framework is ready to pass ++ * platform data to SST driver ++ */ ++#define MRFLD_FW_VIRTUAL_BASE 0xC0000000 ++#define MRFLD_FW_DDR_BASE_OFFSET 0x0 ++#define MRFLD_FW_FEATURE_BASE_OFFSET 0x4 ++#define MRFLD_FW_BSS_RESET_BIT 0 ++extern struct intel_sst_drv *sst_drv_ctx; ++enum sst_states { ++ SST_FW_LOADED = 1, ++ SST_FW_RUNNING, ++ SST_START_INIT, ++ SST_UN_INIT, ++ SST_ERROR, ++ SST_SUSPENDED, ++ SST_FW_CTXT_RESTORE, ++ SST_SHUTDOWN, ++ SST_FW_LIB_LOAD, ++}; ++ ++enum sst_algo_ops { ++ SST_SET_ALGO = 0, ++ SST_GET_ALGO = 1, ++}; ++ ++#define SST_BLOCK_TIMEOUT 1000 ++ ++/* SST register map */ ++#define SST_CSR 0x00 ++#define SST_PISR 0x08 ++#define SST_PIMR 0x10 ++#define SST_ISRX 0x18 ++#define SST_ISRD 0x20 ++#define SST_IMRX 0x28 ++#define SST_IMRD 0x30 ++#define SST_IPCX 0x38 /* IPC IA-SST */ ++#define SST_IPCD 0x40 /* IPC SST-IA */ ++#define SST_ISRSC 0x48 ++#define SST_ISRLPESC 0x50 ++#define SST_IMRSC 0x58 ++#define SST_IMRLPESC 0x60 ++#define SST_IPCSC 0x68 ++#define SST_IPCLPESC 0x70 ++#define SST_CLKCTL 0x78 ++#define SST_CSR2 0x80 ++ ++#define SST_SHIM_BEGIN SST_CSR ++#define SST_SHIM_END SST_CSR2 ++#define SST_SHIM_SIZE 0x88 ++ ++#define FW_SIGNATURE_SIZE 4 ++ ++/* stream states */ ++enum sst_stream_states { ++ STREAM_UN_INIT = 0, /* Freed/Not used stream */ ++ STREAM_RUNNING = 1, /* Running */ ++ STREAM_PAUSED = 2, /* Paused stream */ ++ STREAM_DECODE = 3, /* stream is in decoding only state */ ++ STREAM_INIT = 4, /* stream init, waiting for data */ ++ STREAM_RESET = 5, /* force reset on recovery */ ++}; ++ ++enum sst_ram_type { ++ SST_IRAM = 1, ++ SST_DRAM = 2, ++}; ++ ++/* SST shim registers to structure mapping */ ++union config_status_reg { ++ struct { ++ u32 mfld_strb:1; ++ u32 sst_reset:1; ++ u32 clk_sel:3; ++ u32 sst_clk:2; ++ u32 bypass:3; ++ u32 run_stall:1; ++ u32 rsvd1:2; ++ u32 strb_cntr_rst:1; ++ u32 rsvd:18; ++ } part; ++ u32 full; ++}; ++ ++union interrupt_reg { ++ struct { ++ u64 done_interrupt:1; ++ u64 busy_interrupt:1; ++ u64 rsvd:62; ++ } part; ++ u64 full; ++}; ++ ++union sst_imr_reg { ++ struct { ++ u32 done_interrupt:1; ++ u32 busy_interrupt:1; ++ u32 rsvd:30; ++ } part; ++ u32 full; ++}; ++ ++union sst_pisr_reg { ++ struct { ++ u32 pssp0:1; ++ u32 pssp1:1; ++ u32 rsvd0:3; ++ u32 dmac:1; ++ u32 rsvd1:26; ++ } part; ++ u32 full; ++}; ++ ++union sst_pimr_reg { ++ struct { ++ u32 ssp0:1; ++ u32 ssp1:1; ++ u32 rsvd0:3; ++ u32 dmac:1; ++ u32 rsvd1:10; ++ u32 ssp0_sc:1; ++ u32 ssp1_sc:1; ++ u32 rsvd2:3; ++ u32 dmac_sc:1; ++ u32 rsvd3:10; ++ } part; ++ u32 full; ++}; ++ ++union config_status_reg_mrfld { ++ struct { ++ u64 lpe_reset:1; ++ u64 lpe_reset_vector:1; ++ u64 runstall:1; ++ u64 pwaitmode:1; ++ u64 clk_sel:3; ++ u64 rsvd2:1; ++ u64 sst_clk:3; ++ u64 xt_snoop:1; ++ u64 rsvd3:4; ++ u64 clk_sel1:6; ++ u64 clk_enable:3; ++ u64 rsvd4:6; ++ u64 slim0baseclk:1; ++ u64 rsvd:32; ++ } part; ++ u64 full; ++}; ++ ++union interrupt_reg_mrfld { ++ struct { ++ u64 done_interrupt:1; ++ u64 busy_interrupt:1; ++ u64 rsvd:62; ++ } part; ++ u64 full; ++}; ++ ++union sst_imr_reg_mrfld { ++ struct { ++ u64 done_interrupt:1; ++ u64 busy_interrupt:1; ++ u64 rsvd:62; ++ } part; ++ u64 full; ++}; ++ ++/*This structure is used to block a user/fw data call to another ++fw/user call ++*/ ++struct sst_block { ++ bool condition; /* condition for blocking check */ ++ int ret_code; /* ret code when block is released */ ++ void *data; /* data to be appsed for block if any */ ++ u32 size; ++ bool on; ++ u32 msg_id; /*msg_id = msgid in mfld/ctp, mrfld = 0 */ ++ u32 drv_id; /* = str_id in mfld/ctp, = drv_id in mrfld*/ ++ struct list_head node; ++}; ++ ++/** ++ * struct stream_info - structure that holds the stream information ++ * ++ * @status : stream current state ++ * @prev : stream prev state ++ * @ops : stream operation pb/cp/drm... ++ * @bufs: stream buffer list ++ * @lock : stream mutex for protecting state ++ * @pcm_substream : PCM substream ++ * @period_elapsed : PCM period elapsed callback ++ * @sfreq : stream sampling freq ++ * @str_type : stream type ++ * @cumm_bytes : cummulative bytes decoded ++ * @str_type : stream type ++ * @src : stream source ++ * @device : output device type (medfield only) ++ */ ++struct stream_info { ++ unsigned int status; ++ unsigned int prev; ++ unsigned int ops; ++ struct mutex lock; /* mutex */ ++ void *pcm_substream; ++ void (*period_elapsed) (void *pcm_substream); ++ unsigned int sfreq; ++ u32 cumm_bytes; ++ void *compr_cb_param; ++ void (*compr_cb) (void *compr_cb_param); ++ void *drain_cb_param; ++ void (*drain_notify) (void *drain_cb_param); ++ ++ unsigned int num_ch; ++ unsigned int pipe_id; ++ unsigned int str_id; ++ unsigned int task_id; ++}; ++ ++#define SST_FW_SIGN "$SST" ++#define SST_FW_LIB_SIGN "$LIB" ++ ++/* ++ * struct fw_header - FW file headers ++ * ++ * @signature : FW signature ++ * @modules : # of modules ++ * @file_format : version of header format ++ * @reserved : reserved fields ++ */ ++struct fw_header { ++ unsigned char signature[FW_SIGNATURE_SIZE]; /* FW signature */ ++ u32 file_size; /* size of fw minus this header */ ++ u32 modules; /* # of modules */ ++ u32 file_format; /* version of header format */ ++ u32 reserved[4]; ++}; ++ ++struct fw_module_header { ++ unsigned char signature[FW_SIGNATURE_SIZE]; /* module signature */ ++ u32 mod_size; /* size of module */ ++ u32 blocks; /* # of blocks */ ++ u32 type; /* codec type, pp lib */ ++ u32 entry_point; ++}; ++ ++struct fw_block_info { ++ enum sst_ram_type type; /* IRAM/DRAM */ ++ u32 size; /* Bytes */ ++ u32 ram_offset; /* Offset in I/DRAM */ ++ u32 rsvd; /* Reserved field */ ++}; ++ ++struct sst_ipc_msg_wq { ++ union ipc_header_mrfld mrfld_header; ++ struct ipc_dsp_hdr dsp_hdr; ++ char mailbox[SST_MAILBOX_SIZE]; ++ struct work_struct wq; ++ union ipc_header header; ++}; ++ ++struct sst_dma { ++ struct dma_chan *ch; ++ struct intel_mid_dma_slave slave; ++ struct device *dev; ++}; ++ ++struct sst_runtime_param { ++ struct snd_sst_runtime_params param; ++}; ++ ++struct sst_sg_list { ++ struct scatterlist *src; ++ struct scatterlist *dst; ++ int list_len; ++ unsigned int sg_idx; ++}; ++ ++struct sst_memcpy_list { ++ struct list_head memcpylist; ++ void *dstn; ++ const void *src; ++ u32 size; ++ bool is_io; ++}; ++ ++struct sst_debugfs { ++#ifdef CONFIG_DEBUG_FS ++ struct dentry *root; ++#endif ++ int runtime_pm_status; ++ void __iomem *ssp[SST_MAX_SSP_PORTS]; ++ void __iomem *dma_reg[SST_MAX_DMA]; ++ unsigned char get_params_data[1024]; ++ ssize_t get_params_len; ++}; ++ ++struct lpe_log_buf_hdr { ++ u32 base_addr; ++ u32 end_addr; ++ u32 rd_addr; ++ u32 wr_addr; ++}; ++ ++struct snd_ssp_config { ++ int size; ++ char bytes[0]; ++}; ++ ++struct snd_sst_probe_bytes { ++ u16 len; ++ char bytes[0]; ++}; ++ ++#define PCI_DMAC_CLV_ID 0x08F0 ++#define PCI_DMAC_MRFLD_ID 0x119B ++ ++struct sst_ram_buf { ++ u32 size; ++ char *buf; ++}; ++ ++/* Firmware Module Information*/ ++ ++enum sst_lib_dwnld_status { ++ SST_LIB_NOT_FOUND = 0, ++ SST_LIB_FOUND, ++ SST_LIB_DOWNLOADED, ++}; ++ ++struct sst_module_info { ++ const char *name; /* Library name */ ++ u32 id; /* Module ID */ ++ u32 entry_pt; /* Module entry point */ ++ u8 status; /* module status*/ ++ u8 rsvd1; ++ u16 rsvd2; ++}; ++ ++/* Structure for managing the Library Region(1.5MB) ++ * in DDR in Merrifield ++ */ ++struct sst_mem_mgr { ++ phys_addr_t current_base; ++ int avail; ++ unsigned int count; ++}; ++ ++struct sst_dump_buf { ++ /* buffers for iram-dram dump crash */ ++ struct sst_ram_buf iram_buf; ++ struct sst_ram_buf dram_buf; ++}; ++ ++struct sst_ipc_reg { ++ int ipcx; ++ int ipcd; ++}; ++ ++struct sst_shim_regs64 { ++ u64 csr; ++ u64 pisr; ++ u64 pimr; ++ u64 isrx; ++ u64 isrd; ++ u64 imrx; ++ u64 imrd; ++ u64 ipcx; ++ u64 ipcd; ++ u64 isrsc; ++ u64 isrlpesc; ++ u64 imrsc; ++ u64 imrlpesc; ++ u64 ipcsc; ++ u64 ipclpesc; ++ u64 clkctl; ++ u64 csr2; ++}; ++ ++struct sst_vtsv_cache { ++ void *file1_in_mem; ++ u32 size1; ++ void *file2_in_mem; ++ u32 size2; ++}; ++ ++/*** ++ * ++ * struct intel_sst_drv - driver ops ++ * ++ * @sst_state : current sst device state ++ * @pci_id : PCI device id loaded ++ * @shim : SST shim pointer ++ * @mailbox : SST mailbox pointer ++ * @iram : SST IRAM pointer ++ * @dram : SST DRAM pointer ++ * @pdata : SST info passed as a part of pci platform data ++ * @shim_phy_add : SST shim phy addr ++ * @shim_regs64: Struct to save shim registers ++ * @ipc_dispatch_list : ipc messages dispatched ++ * @rx_list : to copy the process_reply/process_msg from DSP ++ * @ipc_post_msg_wq : wq to post IPC messages context ++ * @ipc_post_msg : wq to post reply from FW context ++ * @mad_ops : MAD driver operations registered ++ * @mad_wq : MAD driver wq ++ * @post_msg_wq : wq to post IPC messages ++ * @streams : sst stream contexts ++ * @list_lock : sst driver list lock (deprecated) ++ * @ipc_spin_lock : spin lock to handle audio shim access and ipc queue ++ * @rx_msg_lock : spin lock to handle the rx messages from the DSP ++ * @scard_ops : sst card ops ++ * @pci : sst pci device struture ++ * @dev : pointer to current device struct ++ * @sst_lock : sst device lock ++ * @stream_lock : sst stream lock ++ * @pvt_id : sst private id ++ * @stream_cnt : total sst active stream count ++ * @pb_streams : total active pb streams ++ * @cp_streams : total active cp streams ++ * @audio_start : audio status ++ * @qos : PM Qos struct ++ * firmware_name : Firmware / Library name ++ */ ++struct intel_sst_drv { ++ int sst_state; ++ int irq_num; ++ unsigned int pci_id; ++ bool use_32bit_ops; ++ void __iomem *ddr; ++ void __iomem *shim; ++ void __iomem *mailbox; ++ void __iomem *iram; ++ void __iomem *dram; ++ unsigned int mailbox_add; ++ unsigned int iram_base; ++ unsigned int dram_base; ++ unsigned int shim_phy_add; ++ unsigned int iram_end; ++ unsigned int dram_end; ++ unsigned int ddr_end; ++ unsigned int ddr_base; ++ unsigned int mailbox_recv_offset; ++ atomic_t pm_usage_count; ++ struct sst_shim_regs64 *shim_regs64; ++ struct list_head block_list; ++ struct list_head ipc_dispatch_list; ++ struct sst_platform_info *pdata; ++ struct sst_ipc_msg_wq ipc_post_msg; ++ struct list_head rx_list; ++ struct work_struct ipc_post_msg_wq; ++ wait_queue_head_t wait_queue; ++ struct workqueue_struct *mad_wq; ++ struct workqueue_struct *post_msg_wq; ++ unsigned int tstamp; ++ struct stream_info streams[MAX_NUM_STREAMS+1]; /*str_id 0 is not used*/ ++ spinlock_t ipc_spin_lock; /* lock for Shim reg access and ipc queue */ ++ spinlock_t block_lock; /* lock for adding block to block_list */ ++ spinlock_t pvt_id_lock; /* lock for allocating private id */ ++ spinlock_t rx_msg_lock; ++ struct pci_dev *pci; ++ struct device *dev; ++ unsigned int pvt_id; ++ struct mutex sst_lock; ++ struct mutex stream_lock; ++ unsigned int stream_cnt; ++ unsigned int *fw_cntx; ++ unsigned int fw_cntx_size; ++ unsigned int csr_value; ++ struct sst_dma dma; ++ void *fw_in_mem; ++ struct sst_runtime_param runtime_param; ++ unsigned int device_input_mixer; ++ struct mutex mixer_ctrl_lock; ++ struct dma_async_tx_descriptor *desc; ++ struct sst_sg_list fw_sg_list, library_list; ++ struct intel_sst_ops *ops; ++ struct sst_debugfs debugfs; ++ struct pm_qos_request *qos; ++ struct sst_info info; ++ unsigned int use_dma; ++ unsigned int use_lli; ++ atomic_t fw_clear_context; ++ atomic_t fw_clear_cache; ++ bool lib_dwnld_reqd; ++ /* list used during FW download in memcpy mode */ ++ struct list_head memcpy_list; ++ /* list used during LIB download in memcpy mode */ ++ struct list_head libmemcpy_list; ++ /* holds the stucts of iram/dram local buffers for dump*/ ++ struct sst_dump_buf dump_buf; ++ /* Lock for CSR register change */ ++ struct mutex csr_lock; ++ /* byte control to set the probe stream */ ++ struct snd_sst_probe_bytes *probe_bytes; ++ /* contains the ipc registers */ ++ struct sst_ipc_reg ipc_reg; ++ /* IMR region Library space memory manager */ ++ struct sst_mem_mgr lib_mem_mgr; ++ /* Contains the cached vtsv files*/ ++ struct sst_vtsv_cache vcache; ++ /* Pointer to device ID, now for same PCI_ID, HID will be ++ * will be different for FDK and EDK2. This will be used ++ * for devices where PCI or ACPI id is same but HID is ++ * different ++ */ ++ const char *hid; ++ /* Holder for firmware name. Due to async call it needs to be ++ * persistent till worker thread gets called ++ */ ++ char firmware_name[20]; ++}; ++ ++extern struct intel_sst_drv *sst_drv_ctx; ++extern struct sst_platform_info byt_rvp_platform_data; ++extern struct sst_platform_info byt_ffrd8_platform_data; ++extern struct sst_platform_info cht_platform_data; ++ ++/* misc definitions */ ++#define FW_DWNL_ID 0xFF ++ ++struct sst_fill_config { ++ u32 sign; ++ struct sst_board_config_data sst_bdata; ++ struct sst_platform_config_data sst_pdata; ++ u32 shim_phy_add; ++ u32 mailbox_add; ++} __packed; ++ ++struct intel_sst_ops { ++ irqreturn_t (*interrupt) (int, void *); ++ irqreturn_t (*irq_thread) (int, void *); ++ void (*clear_interrupt) (void); ++ int (*start) (void); ++ int (*reset) (void); ++ void (*process_reply) (struct ipc_post *msg); ++ void (*post_message) (struct work_struct *work); ++ int (*sync_post_message) (struct ipc_post *msg); ++ void (*process_message) (struct ipc_post *msg); ++ void (*set_bypass)(bool set); ++ int (*save_dsp_context) (struct intel_sst_drv *sst); ++ void (*restore_dsp_context) (void); ++ int (*alloc_stream) (char *params, struct sst_block *block); ++ void (*post_download)(struct intel_sst_drv *sst); ++ void (*do_recovery)(struct intel_sst_drv *sst); ++}; ++ ++int sst_alloc_stream(char *params, struct sst_block *block); ++int sst_pause_stream(int id); ++int sst_resume_stream(int id); ++int sst_drop_stream(int id); ++int sst_next_track(void); ++int sst_free_stream(int id); ++int sst_start_stream(int str_id); ++int sst_send_byte_stream_mrfld(void *sbytes); ++int sst_send_probe_bytes(struct intel_sst_drv *sst); ++int sst_set_stream_param(int str_id, struct snd_sst_params *str_param); ++int sst_set_metadata(int str_id, char *params); ++int sst_get_stream(struct snd_sst_params *str_param); ++int sst_get_stream_allocated(struct snd_sst_params *str_param, ++ struct snd_sst_lib_download **lib_dnld); ++int sst_drain_stream(int str_id, bool partial_drain); ++ ++ ++int sst_sync_post_message_mfld(struct ipc_post *msg); ++void sst_post_message_mfld(struct work_struct *work); ++void sst_process_message_mfld(struct ipc_post *msg); ++void sst_process_reply_mfld(struct ipc_post *msg); ++int sst_start_mfld(void); ++int intel_sst_reset_dsp_mfld(void); ++void intel_sst_clear_intr_mfld(void); ++void intel_sst_set_bypass_mfld(bool set); ++ ++int sst_sync_post_message_mrfld(struct ipc_post *msg); ++void sst_post_message_mrfld(struct work_struct *work); ++void sst_process_message_mrfld(struct ipc_post *msg); ++void sst_process_reply_mrfld(struct ipc_post *msg); ++int sst_start_mrfld(void); ++int intel_sst_reset_dsp_mrfld(void); ++void intel_sst_clear_intr_mrfld(void); ++void sst_process_mad_ops(struct work_struct *work); ++ ++long intel_sst_ioctl(struct file *file_ptr, unsigned int cmd, ++ unsigned long arg); ++int intel_sst_open_cntrl(struct inode *i_node, struct file *file_ptr); ++int intel_sst_release_cntrl(struct inode *i_node, struct file *file_ptr); ++ ++int sst_load_fw(void); ++int sst_load_library(struct snd_sst_lib_download *lib, u8 ops); ++int sst_load_all_modules_elf(struct intel_sst_drv *ctx, ++ struct sst_module_info *mod_table, int mod_table_size); ++int sst_get_next_lib_mem(struct sst_mem_mgr *mgr, int size, ++ unsigned long *lib_base); ++void sst_post_download_ctp(struct intel_sst_drv *ctx); ++void sst_post_download_mrfld(struct intel_sst_drv *ctx); ++void sst_post_download_byt(struct intel_sst_drv *ctx); ++int sst_get_block_stream(struct intel_sst_drv *sst_drv_ctx); ++void sst_memcpy_free_resources(void); ++ ++int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx, ++ struct sst_block *block); ++int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx, ++ struct sst_block *block); ++int sst_create_ipc_msg(struct ipc_post **arg, bool large); ++int sst_download_fw(void); ++int free_stream_context(unsigned int str_id); ++void sst_clean_stream(struct stream_info *stream); ++int intel_sst_register_compress(struct intel_sst_drv *sst); ++int intel_sst_remove_compress(struct intel_sst_drv *sst); ++void sst_cdev_fragment_elapsed(int str_id); ++int sst_send_sync_msg(int ipc, int str_id); ++int sst_get_num_channel(struct snd_sst_params *str_param); ++int sst_get_sfreq(struct snd_sst_params *str_param); ++int intel_sst_check_device(void); ++int sst_alloc_stream_ctp(char *params, struct sst_block *block); ++int sst_alloc_stream_mrfld(char *params, struct sst_block *block); ++void sst_restore_fw_context(void); ++struct sst_block *sst_create_block(struct intel_sst_drv *ctx, ++ u32 msg_id, u32 drv_id); ++int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large, ++ struct intel_sst_drv *sst_drv_ctx, struct sst_block **block, ++ u32 msg_id, u32 drv_id); ++int sst_free_block(struct intel_sst_drv *ctx, struct sst_block *freed); ++int sst_wake_up_block(struct intel_sst_drv *ctx, int result, ++ u32 drv_id, u32 ipc, void *data, u32 size); ++int sst_alloc_drv_context(struct device *dev); ++int sst_request_firmware_async(struct intel_sst_drv *ctx); ++int sst_driver_ops(struct intel_sst_drv *sst); ++struct sst_platform_info *sst_get_acpi_driver_data(const char *hid); ++int sst_acpi_probe(struct platform_device *pdev); ++int sst_acpi_remove(struct platform_device *pdev); ++void sst_save_shim64(struct intel_sst_drv *ctx, void __iomem *shim, ++ struct sst_shim_regs64 *shim_regs); ++void sst_firmware_load_cb(const struct firmware *fw, void *context); ++int sst_send_vtsv_data_to_fw(struct intel_sst_drv *ctx); ++ ++void sst_do_recovery_mrfld(struct intel_sst_drv *sst); ++void sst_do_recovery(struct intel_sst_drv *sst); ++long intel_sst_ioctl_dsp(unsigned int cmd, ++ struct snd_ppp_params *algo_params, unsigned long arg); ++ ++void sst_dump_to_buffer(const void *from, size_t from_len, char *buf); ++ ++extern int intel_scu_ipc_simple_command(int, int); ++ ++static inline int sst_pm_runtime_put(struct intel_sst_drv *sst_drv) ++{ ++ int ret; ++ ++ ret = pm_runtime_put_sync(sst_drv->dev); ++ if (ret < 0) ++ return ret; ++ atomic_dec(&sst_drv->pm_usage_count); ++ ++ pr_debug("%s: count is %d now..\n", __func__, ++ atomic_read(&sst_drv->pm_usage_count)); ++ return 0; ++} ++/* ++ * sst_fill_header - inline to fill sst header ++ * ++ * @header : ipc header ++ * @msg : IPC message to be sent ++ * @large : is ipc large msg ++ * @str_id : stream id ++ * ++ * this function is an inline function that sets the headers before ++ * sending a message ++ */ ++static inline void sst_fill_header(union ipc_header *header, ++ int msg, int large, int str_id) ++{ ++ header->part.msg_id = msg; ++ header->part.str_id = str_id; ++ header->part.large = large; ++ header->part.done = 0; ++ header->part.busy = 1; ++ header->part.data = 0; ++} ++ ++ ++static inline void sst_fill_header_mrfld(union ipc_header_mrfld *header, ++ int msg, int task_id, int large, int drv_id) ++{ ++ header->full = 0; ++ header->p.header_high.part.msg_id = msg; ++ header->p.header_high.part.task_id = task_id; ++ header->p.header_high.part.large = large; ++ header->p.header_high.part.drv_id = drv_id; ++ header->p.header_high.part.done = 0; ++ header->p.header_high.part.busy = 1; ++ header->p.header_high.part.res_rqd = 1; ++} ++ ++static inline void sst_fill_header_dsp(struct ipc_dsp_hdr *dsp, int msg, ++ int pipe_id, int len) ++{ ++ dsp->cmd_id = msg; ++ dsp->mod_index_id = 0xff; ++ dsp->pipe_id = pipe_id; ++ dsp->length = len; ++ dsp->mod_id = 0; ++} ++ ++#define MAX_BLOCKS 15 ++/* sst_assign_pvt_id - assign a pvt id for stream ++ * ++ * @sst_drv_ctx : driver context ++ * ++ * this inline function assigns a private id for calls that dont have stream ++ * context yet, should be called with lock held ++ */ ++static inline unsigned int sst_assign_pvt_id(struct intel_sst_drv *sst_drv_ctx) ++{ ++ unsigned int local; ++ ++ spin_lock(&sst_drv_ctx->pvt_id_lock); ++ sst_drv_ctx->pvt_id++; ++ if (sst_drv_ctx->pvt_id > MAX_BLOCKS) ++ sst_drv_ctx->pvt_id = 1; ++ local = sst_drv_ctx->pvt_id; ++ spin_unlock(&sst_drv_ctx->pvt_id_lock); ++ return local; ++} ++ ++ ++/* ++ * sst_init_stream - this function initialzes stream context ++ * ++ * @stream : stream struture ++ * @codec : codec for stream ++ * @sst_id : stream id ++ * @ops : stream operation ++ * @slot : stream pcm slot ++ * @device : device type ++ * ++ * this inline function initialzes stream context for allocated stream ++ */ ++static inline void sst_init_stream(struct stream_info *stream, ++ int codec, int sst_id, int ops, u8 slot) ++{ ++ stream->status = STREAM_INIT; ++ stream->prev = STREAM_UN_INIT; ++ stream->ops = ops; ++} ++ ++static inline void sst_set_gpio_conf(const struct sst_gpio_config *gpio_conf) ++{ ++ lnw_gpio_set_alt(gpio_conf->i2s_rx_alt, gpio_conf->alt_function); ++ lnw_gpio_set_alt(gpio_conf->i2s_tx_alt, gpio_conf->alt_function); ++ lnw_gpio_set_alt(gpio_conf->i2s_frame, gpio_conf->alt_function); ++ lnw_gpio_set_alt(gpio_conf->i2s_clock, gpio_conf->alt_function); ++} ++ ++ ++/* ++ * sst_validate_strid - this function validates the stream id ++ * ++ * @str_id : stream id to be validated ++ * ++ * returns 0 if valid stream ++ */ ++static inline int sst_validate_strid(int str_id) ++{ ++ if (str_id <= 0 || str_id > sst_drv_ctx->info.max_streams) { ++ pr_err("SST ERR: invalid stream id : %d, max %d\n", ++ str_id, sst_drv_ctx->info.max_streams); ++ return -EINVAL; ++ } else ++ return 0; ++} ++ ++static inline int sst_shim_write(void __iomem *addr, int offset, int value) ++{ ++ writel(value, addr + offset); ++ return 0; ++} ++ ++static inline u32 sst_shim_read(void __iomem *addr, int offset) ++{ ++ ++ return readl(addr + offset); ++} ++ ++static inline u32 sst_reg_read(void __iomem *addr, int offset) ++{ ++ ++ return readl(addr + offset); ++} ++ ++static inline u64 sst_reg_read64(void __iomem *addr, int offset) ++{ ++ u64 val = 0; ++ ++ memcpy_fromio(&val, addr + offset, sizeof(val)); ++ ++ return val; ++} ++ ++static inline int sst_shim_write64(void __iomem *addr, int offset, u64 value) ++{ ++ memcpy_toio(addr + offset, &value, sizeof(value)); ++ return 0; ++} ++ ++static inline u64 sst_shim_read64(void __iomem *addr, int offset) ++{ ++ u64 val = 0; ++ ++ memcpy_fromio(&val, addr + offset, sizeof(val)); ++ return val; ++} ++ ++static inline void ++sst_set_fw_state_locked(struct intel_sst_drv *sst_drv_ctx, int sst_state) ++{ ++ mutex_lock(&sst_drv_ctx->sst_lock); ++ sst_drv_ctx->sst_state = sst_state; ++ mutex_unlock(&sst_drv_ctx->sst_lock); ++} ++ ++static inline struct stream_info *get_stream_info(int str_id) ++{ ++ if (sst_validate_strid(str_id)) ++ return NULL; ++ return &sst_drv_ctx->streams[str_id]; ++} ++ ++static inline int get_stream_id_mrfld(u32 pipe_id) ++{ ++ int i; ++ ++ for (i = 1; i <= sst_drv_ctx->info.max_streams; i++) ++ if (pipe_id == sst_drv_ctx->streams[i].pipe_id) ++ return i; ++ ++ pr_debug("%s: no such pipe_id(%u)", __func__, pipe_id); ++ return -1; ++} ++ ++int register_sst(struct device *); ++int unregister_sst(struct device *); ++ ++#ifdef CONFIG_DEBUG_FS ++void sst_debugfs_init(struct intel_sst_drv *sst); ++void sst_debugfs_exit(struct intel_sst_drv *sst); ++#else ++static inline void sst_debugfs_init(struct intel_sst_drv *sst) ++{ ++} ++ ++static inline void sst_debugfs_exit(struct intel_sst_drv *sst) ++{ ++} ++#endif /* CONFIG_DEBUG_FS */ ++ ++/* ++ * FW should use virtual address 0xC000_0000 to map to the DDR ++ * reserved 2MB region at 512MB boundary. Currently the address of ++ * DDR region allocated by IA FW is not 512MB aligned. So FW is ++ * statically linking the DDR region at 0xDF600000. So we need to ++ * use the translated address to identify the DDR regions in the FW ++ * ELF binary. ++ */ ++static inline u32 relocate_imr_addr_mrfld(u32 base_addr) ++{ ++ /* Get the difference from 512MB aligned base addr */ ++ /* relocate the base */ ++ base_addr = MRFLD_FW_VIRTUAL_BASE + (base_addr % (512 * 1024 * 1024)); ++ return base_addr; ++} ++ ++static inline void sst_add_to_dispatch_list_and_post(struct intel_sst_drv *sst, ++ struct ipc_post *msg) ++{ ++ unsigned long irq_flags; ++ spin_lock_irqsave(&sst->ipc_spin_lock, irq_flags); ++ list_add_tail(&msg->node, &sst->ipc_dispatch_list); ++ spin_unlock_irqrestore(&sst->ipc_spin_lock, irq_flags); ++ sst->ops->post_message(&sst->ipc_post_msg_wq); ++} ++#endif +diff --git a/sound/soc/intel/sst/sst_acpi.c b/sound/soc/intel/sst/sst_acpi.c +new file mode 100644 +index 0000000..a898c2a +--- /dev/null ++++ b/sound/soc/intel/sst/sst_acpi.c +@@ -0,0 +1,669 @@ ++/* sst_acpi.c - SST (LPE) driver init file for ACPI enumeration. ++ * ++ * Copyright (c) 2013, Intel Corporation. ++ * ++ * Authors: Ramesh Babu K V ++ * Authors: Omair Mohammed Abdullah ++ * ++ * 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, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "../sst_platform.h" ++#include "../platform_ipc_v2.h" ++#include "sst.h" ++ ++extern struct miscdevice lpe_ctrl; ++ ++static const struct sst_platform_config_data sst_byt_pdata = { ++ .sst_sram_buff_base = 0xffffffff, ++ .sst_dma_base[0] = SST_BYT_DMA0_PHY_ADDR, ++ .sst_dma_base[1] = SST_BYT_DMA1_PHY_ADDR, ++}; ++ ++/* use array[0] for ssp_platform_data even though SSP2 is used */ ++static const struct sst_board_config_data sst_byt_rvp_bdata = { ++ .active_ssp_ports = 1, ++ .platform_id = 3, ++ .board_id = 1, ++ .ihf_num_chan = 2, ++ .osc_clk_freq = 25000000, ++ .ssp_platform_data = { ++ [0] = { ++ .ssp_cfg_sst = 1, ++ .port_number = 2, ++ .is_master = 1, ++ .pack_mode = 1, ++ .num_slots_per_frame = 2, ++ .num_bits_per_slot = 24, ++ .active_tx_map = 3, ++ .active_rx_map = 3, ++ .ssp_frame_format = 3, ++ .frame_polarity = 1, ++ .serial_bitrate_clk_mode = 0, ++ .frame_sync_width = 24, ++ .dma_handshake_interface_tx = 5, ++ .dma_handshake_interface_rx = 4, ++ .network_mode = 0, ++ .start_delay = 1, ++ .ssp_base_add = SST_BYT_SSP2_PHY_ADDR, ++ }, ++ }, ++}; ++ ++static const struct sst_board_config_data sst_byt_ffrd8_bdata = { ++ .active_ssp_ports = 1, ++ .platform_id = 3, ++ .board_id = 1, ++ .ihf_num_chan = 2, ++ .osc_clk_freq = 25000000, ++ .ssp_platform_data = { ++ [0] = { ++ .ssp_cfg_sst = 1, ++ .port_number = 0, ++ .is_master = 1, ++ .pack_mode = 1, ++ .num_slots_per_frame = 2, ++ .num_bits_per_slot = 24, ++ .active_tx_map = 3, ++ .active_rx_map = 3, ++ .ssp_frame_format = 3, ++ .frame_polarity = 1, ++ .serial_bitrate_clk_mode = 0, ++ .frame_sync_width = 24, ++ .dma_handshake_interface_tx = 1, ++ .dma_handshake_interface_rx = 0, ++ .network_mode = 0, ++ .start_delay = 1, ++ .ssp_base_add = SST_BYT_SSP0_PHY_ADDR, ++ }, ++ }, ++}; ++ ++static const struct sst_board_config_data sst_byt_crv2_bdata = { ++ .active_ssp_ports = 1, ++ .platform_id = 3, ++ .board_id = 1, ++ .ihf_num_chan = 1, ++ .osc_clk_freq = 25000000, ++ .ssp_platform_data = { ++ [0] = { ++ .ssp_cfg_sst = 1, ++ .port_number = 0, ++ .is_master = 1, ++ .pack_mode = 1, ++ .num_slots_per_frame = 2, ++ .num_bits_per_slot = 24, ++ .active_tx_map = 3, ++ .active_rx_map = 3, ++ .ssp_frame_format = 3, ++ .frame_polarity = 1, ++ .serial_bitrate_clk_mode = 0, ++ .frame_sync_width = 24, ++ .dma_handshake_interface_tx = 1, ++ .dma_handshake_interface_rx = 0, ++ .network_mode = 0, ++ .start_delay = 1, ++ .ssp_base_add = SST_BYT_SSP0_PHY_ADDR, ++ }, ++ }, ++}; ++ ++static const struct sst_info byt_fwparse_info = { ++ .use_elf = true, ++ .max_streams = 4, ++ .dma_max_len = SST_MAX_DMA_LEN_MRFLD, ++ .iram_start = SST_BYT_IRAM_PHY_START, ++ .iram_end = SST_BYT_IRAM_PHY_END, ++ .iram_use = true, ++ .dram_start = SST_BYT_DRAM_PHY_START, ++ .dram_end = SST_BYT_DRAM_PHY_END, ++ .dram_use = true, ++ .imr_start = SST_BYT_IMR_VIRT_START, ++ .imr_end = SST_BYT_IMR_VIRT_END, ++ .imr_use = true, ++ .mailbox_start = SST_BYT_MBOX_PHY_ADDR, ++ .num_probes = 0, ++ .lpe_viewpt_rqd = true, ++}; ++ ++ ++static const struct sst_info cht_fwparse_info = { ++ .use_elf = true, ++ .max_streams = MAX_NUM_STREAMS_MRFLD, ++ .dma_max_len = SST_MAX_DMA_LEN_MRFLD, ++ .iram_start = SST_BYT_IRAM_PHY_START, ++ .iram_end = SST_BYT_IRAM_PHY_END, ++ .iram_use = true, ++ .dram_start = SST_BYT_DRAM_PHY_START, ++ .dram_end = SST_BYT_DRAM_PHY_END, ++ .dram_use = true, ++ .imr_start = SST_BYT_IMR_VIRT_START, ++ .imr_end = SST_BYT_IMR_VIRT_END, ++ .imr_use = true, ++ .mailbox_start = SST_BYT_MBOX_PHY_ADDR, ++ .num_probes = 0, ++ .lpe_viewpt_rqd = true, ++}; ++ ++static const struct sst_ipc_info byt_ipc_info = { ++ .use_32bit_ops = true, ++ .ipc_offset = 4, ++ .mbox_recv_off = 0x400, ++}; ++ ++static const struct sst_lib_dnld_info byt_lib_dnld_info = { ++ .mod_base = SST_BYT_IMR_VIRT_START, ++ .mod_end = SST_BYT_IMR_VIRT_END, ++ .mod_table_offset = BYT_FW_MOD_TABLE_OFFSET, ++ .mod_table_size = BYT_FW_MOD_TABLE_SIZE, ++ .mod_ddr_dnld = true, ++}; ++ ++static const struct sst_ipc_info cht_ipc_info = { ++ .use_32bit_ops = false, ++ .ipc_offset = 0, ++ .mbox_recv_off = 0x400, ++}; ++ ++struct sst_platform_info cht_platform_data = { ++ .probe_data = &cht_fwparse_info, ++ .ssp_data = NULL, ++ .bdata = NULL, ++ .pdata = NULL, ++ .ipc_info = &cht_ipc_info, ++ .lib_info = NULL, ++}; ++ ++struct sst_platform_info byt_rvp_platform_data = { ++ .probe_data = &byt_fwparse_info, ++ .ssp_data = NULL, ++ .bdata = &sst_byt_rvp_bdata, ++ .pdata = &sst_byt_pdata, ++ .ipc_info = &byt_ipc_info, ++ .lib_info = &byt_lib_dnld_info, ++}; ++ ++struct sst_platform_info byt_ffrd8_platform_data = { ++ .probe_data = &byt_fwparse_info, ++ .ssp_data = NULL, ++ .bdata = &sst_byt_ffrd8_bdata, ++ .pdata = &sst_byt_pdata, ++ .ipc_info = &byt_ipc_info, ++ .lib_info = &byt_lib_dnld_info, ++}; ++ ++int sst_workqueue_init(struct intel_sst_drv *ctx) ++{ ++ pr_debug("%s", __func__); ++ ++ INIT_LIST_HEAD(&ctx->memcpy_list); ++ INIT_LIST_HEAD(&ctx->libmemcpy_list); ++ INIT_LIST_HEAD(&sst_drv_ctx->rx_list); ++ INIT_LIST_HEAD(&ctx->ipc_dispatch_list); ++ INIT_LIST_HEAD(&ctx->block_list); ++ INIT_WORK(&ctx->ipc_post_msg.wq, ctx->ops->post_message); ++ init_waitqueue_head(&ctx->wait_queue); ++ ++ ctx->mad_wq = create_singlethread_workqueue("sst_mad_wq"); ++ if (!ctx->mad_wq) ++ goto err_wq; ++ ctx->post_msg_wq = ++ create_singlethread_workqueue("sst_post_msg_wq"); ++ if (!ctx->post_msg_wq) ++ goto err_wq; ++ return 0; ++err_wq: ++ return -EBUSY; ++} ++ ++void sst_init_locks(struct intel_sst_drv *ctx) ++{ ++ mutex_init(&ctx->stream_lock); ++ mutex_init(&ctx->sst_lock); ++ mutex_init(&ctx->mixer_ctrl_lock); ++ mutex_init(&ctx->csr_lock); ++ spin_lock_init(&sst_drv_ctx->rx_msg_lock); ++ spin_lock_init(&ctx->ipc_spin_lock); ++ spin_lock_init(&ctx->block_lock); ++ spin_lock_init(&ctx->pvt_id_lock); ++} ++ ++int sst_destroy_workqueue(struct intel_sst_drv *ctx) ++{ ++ pr_debug("%s", __func__); ++ if (ctx->mad_wq) ++ destroy_workqueue(ctx->mad_wq); ++ if (ctx->post_msg_wq) ++ destroy_workqueue(ctx->post_msg_wq); ++ return 0; ++} ++ ++#if IS_ENABLED(CONFIG_ACPI) ++static int sst_platform_get_resources_fdk(struct intel_sst_drv *ctx, ++ struct platform_device *pdev) ++{ ++ struct resource *rsrc; ++ ++ pr_debug("%s", __func__); ++ ++ /* All ACPI resource request here */ ++ /* Get DDR addr from platform resource table */ ++ rsrc = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!rsrc) { ++ pr_err("Invalid DDR base from IFWI"); ++ return -EIO; ++ } ++ ctx->ddr_base = rsrc->start; ++ ctx->ddr_end = rsrc->end; ++ pr_debug("DDR base: %#x", ctx->ddr_base); ++ ctx->ddr = devm_ioremap_nocache(ctx->dev, ctx->ddr_base, ++ resource_size(rsrc)); ++ if (!ctx->ddr) { ++ pr_err("unable to map DDR"); ++ return -EIO; ++ } ++ ++ /* Get Shim addr from platform resource table */ ++ rsrc = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ if (!rsrc) { ++ pr_err("Invalid SHIM base from IFWI"); ++ return -EIO; ++ } ++ ctx->shim_phy_add = rsrc->start; ++ pr_debug("SHIM base: %#x", ctx->shim_phy_add); ++ ctx->shim = devm_ioremap_nocache(ctx->dev, ctx->shim_phy_add, ++ resource_size(rsrc)); ++ if (!ctx->shim) { ++ pr_err("unable to map SHIM"); ++ return -EIO; ++ } ++ /* reassign physical address to LPE viewpoint address */ ++ ctx->shim_phy_add = SST_BYT_SHIM_PHY_ADDR; ++ ++ /* Get mailbox addr from platform resource table */ ++ rsrc = platform_get_resource(pdev, IORESOURCE_MEM, 2); ++ if (!rsrc) { ++ pr_err("Invalid Mailbox base from IFWI"); ++ return -EIO; ++ } ++ ctx->mailbox_add = rsrc->start; ++ pr_debug("Mailbox base: %#x", ctx->mailbox_add); ++ ctx->mailbox = devm_ioremap_nocache(ctx->dev, ctx->mailbox_add, ++ resource_size(rsrc)); ++ if (!ctx->mailbox) { ++ pr_err("unable to map mailbox"); ++ return -EIO; ++ } ++ /* reassign physical address to LPE viewpoint address */ ++ ctx->mailbox_add = sst_drv_ctx->info.mailbox_start; ++ ++ /* Get iram/iccm addr from platform resource table */ ++ rsrc = platform_get_resource(pdev, IORESOURCE_MEM, 3); ++ if (!rsrc) { ++ pr_err("Invalid IRAM base from IFWI"); ++ return -EIO; ++ } ++ ctx->iram_base = rsrc->start; ++ ctx->iram_end = rsrc->end; ++ pr_debug("IRAM base: %#x", ctx->iram_base); ++ ctx->iram = devm_ioremap_nocache(ctx->dev, ctx->iram_base, ++ resource_size(rsrc)); ++ if (!ctx->iram) { ++ pr_err("unable to map IRAM"); ++ return -EIO; ++ } ++ ++ /* Get dram/dccm addr from platform resource table */ ++ rsrc = platform_get_resource(pdev, IORESOURCE_MEM, 4); ++ if (!rsrc) { ++ pr_err("Invalid DRAM base from IFWI"); ++ return -EIO; ++ } ++ ctx->dram_base = rsrc->start; ++ ctx->dram_end = rsrc->end; ++ pr_debug("DRAM base: %#x", ctx->dram_base); ++ ctx->dram = devm_ioremap_nocache(ctx->dev, ctx->dram_base, ++ resource_size(rsrc)); ++ if (!ctx->dram) { ++ pr_err("unable to map DRAM"); ++ return -EIO; ++ } ++ ++ /* Register the ISR */ ++ ctx->irq_num = platform_get_irq(pdev, 0); ++ pr_debug("irq from pdev is:%d", ctx->irq_num); ++ return 0; ++} ++ ++#define LPE_IRAM_OFFSET 0x0C0000 ++#define LPE_IRAM_SIZE 0x040000 ++#define LPE_DRAM_OFFSET 0x100000 ++#define LPE_DRAM_SIZE 0x040000 ++#define LPE_SHIM_OFFSET 0x140000 ++#define LPE_SHIM_SIZE 0x004000 ++#define LPE_MBOX_OFFSET 0x144000 ++#define LPE_MBOX_SIZE 0x004000 ++ ++static int sst_platform_get_resources_edk(struct intel_sst_drv *ctx, ++ struct platform_device *pdev) ++{ ++ struct resource *rsrc; ++ ++ pr_debug("%s", __func__); ++ ++ /* All ACPI resource request here */ ++ /* Get Shim addr */ ++ rsrc = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!rsrc) { ++ pr_err("Invalid SHIM base from IFWI"); ++ return -EIO; ++ } ++ pr_debug("LPE base: %#x size:%#x", (unsigned int) rsrc->start, ++ (unsigned int)resource_size(rsrc)); ++ ctx->iram_base = rsrc->start + LPE_IRAM_OFFSET; ++ ctx->iram_end = ctx->iram_base + LPE_IRAM_SIZE - 1; ++ pr_debug("IRAM base: %#x", ctx->iram_base); ++ ctx->iram = devm_ioremap_nocache(ctx->dev, ctx->iram_base, ++ LPE_IRAM_SIZE); ++ if (!ctx->iram) { ++ pr_err("unable to map IRAM"); ++ return -EIO; ++ } ++ ++ ctx->dram_base = rsrc->start + LPE_DRAM_OFFSET; ++ ctx->dram_end = ctx->dram_base + LPE_DRAM_SIZE - 1; ++ pr_debug("DRAM base: %#x", ctx->dram_base); ++ ctx->dram = devm_ioremap_nocache(ctx->dev, ctx->dram_base, ++ LPE_DRAM_SIZE); ++ if (!ctx->dram) { ++ pr_err("unable to map DRAM"); ++ return -EIO; ++ } ++ ++ ctx->shim_phy_add = rsrc->start + LPE_SHIM_OFFSET; ++ pr_debug("SHIM base: %#x", ctx->shim_phy_add); ++ ctx->shim = devm_ioremap_nocache(ctx->dev, ctx->shim_phy_add, ++ LPE_SHIM_SIZE); ++ if (!ctx->shim) { ++ pr_err("unable to map SHIM"); ++ return -EIO; ++ } ++ /* reassign physical address to LPE viewpoint address */ ++ ctx->shim_phy_add = SST_BYT_SHIM_PHY_ADDR; ++ ++ /* Get mailbox addr */ ++ ctx->mailbox_add = rsrc->start + LPE_MBOX_OFFSET; ++ pr_debug("Mailbox base: %#x", ctx->mailbox_add); ++ ctx->mailbox = devm_ioremap_nocache(ctx->dev, ctx->mailbox_add, ++ LPE_MBOX_SIZE); ++ if (!ctx->mailbox) { ++ pr_err("unable to map mailbox"); ++ return -EIO; ++ } ++ ++ /* reassign physical address to LPE viewpoint address */ ++ ctx->mailbox_add = sst_drv_ctx->info.mailbox_start; ++ ++ rsrc = platform_get_resource(pdev, IORESOURCE_MEM, 2); ++ if (!rsrc) { ++ pr_err("Invalid DDR base from IFWI"); ++ return -EIO; ++ } ++ ctx->ddr_base = rsrc->start; ++ ctx->ddr_end = rsrc->end; ++ pr_debug("DDR base: %#x", ctx->ddr_base); ++ ctx->ddr = devm_ioremap_nocache(ctx->dev, ctx->ddr_base, ++ resource_size(rsrc)); ++ if (!ctx->ddr) { ++ pr_err("unable to map DDR"); ++ return -EIO; ++ } ++ /* Register the ISR */ ++ if (!strncmp(ctx->hid, "80860F28", 8)) ++ ctx->irq_num = platform_get_irq(pdev, 0); ++ else if (!strncmp(ctx->hid, "808622A8", 8)) { ++ /* FIXME: IRQ number will be moved to 0 once the BIOS fix is done */ ++ ctx->irq_num = platform_get_irq(pdev, 5); ++ } else ++ return -EINVAL; ++ return 0; ++} ++ ++static int sst_platform_get_resources(const char *hid, ++ struct intel_sst_drv *ctx, struct platform_device *pdev) ++{ ++ ++ pr_debug("%s", __func__); ++ ++ if (!strncmp(hid, "LPE0F281", 8)) { ++ ctx->pci_id = SST_BYT_PCI_ID; ++ return sst_platform_get_resources_fdk(ctx, pdev); ++ } ++ if (!strncmp(hid, "808622A8", 8)) { ++ ctx->pci_id = SST_CHT_PCI_ID; ++ return sst_platform_get_resources_edk(ctx, pdev); ++ } ++ if (!strncmp(hid, "80860F28", 8)) { ++ ctx->pci_id = SST_BYT_PCI_ID; ++ return sst_platform_get_resources_edk(ctx, pdev); ++ } else if (!strncmp(hid, "LPE0F28", 7)) { ++ ctx->pci_id = SST_BYT_PCI_ID; ++ return sst_platform_get_resources_fdk(ctx, pdev); ++ } else { ++ pr_err("Invalid device\n"); ++ return -EINVAL; ++ } ++} ++ ++int sst_acpi_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ acpi_handle handle = ACPI_HANDLE(dev); ++ struct acpi_device *device; ++ const char *hid; ++ int i, ret = 0; ++ struct intel_sst_drv *ctx; ++ ++ ret = acpi_bus_get_device(handle, &device); ++ if (ret) { ++ pr_err("%s: could not get acpi device - %d\n", __func__, ret); ++ return -ENODEV; ++ } ++ ++ if (acpi_bus_get_status(device) || !device->status.present) { ++ pr_err("%s: device has invalid status", __func__); ++ return -ENODEV; ++ } ++ ++ hid = acpi_device_hid(device); ++ pr_debug("%s for %s", __func__, hid); ++ ret = sst_alloc_drv_context(dev); ++ if (ret) ++ return ret; ++ ctx = sst_drv_ctx; ++ ctx->dev = dev; ++ ctx->hid = hid; ++ ++ ret = sst_platform_get_resources(hid, ctx, pdev); ++ if (ret) ++ return ret; ++ /* need to save shim registers in BYT */ ++ ctx->shim_regs64 = devm_kzalloc(dev, sizeof(*ctx->shim_regs64), ++ GFP_KERNEL); ++ if (!ctx->shim_regs64) ++ return -ENOMEM; ++ ++ ret = sst_driver_ops(ctx); ++ if (ret != 0) ++ return -EINVAL; ++ ++ sst_init_locks(ctx); ++ ++ ctx->stream_cnt = 0; ++ ctx->fw_in_mem = NULL; ++ ctx->use_dma = 1; ++ ctx->use_lli = 1; ++ ++ if (sst_workqueue_init(ctx)) ++ goto do_free_wq; ++ ++ ctx->pdata = sst_get_acpi_driver_data(hid); ++ if (!ctx->pdata) ++ return -EINVAL; ++ if (INTEL_MID_BOARD(3, TABLET, BYT, BLK, PRO, CRV2)) { ++ /* BYT-CR V2 has only mono speaker, while ++ * byt has stereo speaker, for both ++ * HID is same, so platform data also is ++ * same, hence overriding bdata based on spid ++ */ ++ ctx->pdata->bdata = &sst_byt_crv2_bdata; ++ pr_info("Overriding bdata for byt-crv2\n"); ++ } ++ ++ ctx->use_32bit_ops = ctx->pdata->ipc_info->use_32bit_ops; ++ ctx->mailbox_recv_offset = ctx->pdata->ipc_info->mbox_recv_off; ++ ++ memcpy(&ctx->info, ctx->pdata->probe_data, sizeof(ctx->info)); ++ ++ ctx->ipc_reg.ipcx = SST_IPCX + ctx->pdata->ipc_info->ipc_offset; ++ ctx->ipc_reg.ipcd = SST_IPCD + ctx->pdata->ipc_info->ipc_offset; ++ ++ pr_debug("Got drv data max stream %d\n", ++ ctx->info.max_streams); ++ for (i = 1; i <= ctx->info.max_streams; i++) { ++ struct stream_info *stream = &ctx->streams[i]; ++ mutex_init(&stream->lock); ++ } ++ ret = sst_request_firmware_async(ctx); ++ if (ret) { ++ pr_err("Firmware download failed:%d\n", ret); ++ goto do_free_wq; ++ } ++ ++ ret = devm_request_threaded_irq(ctx->dev, ctx->irq_num, ctx->ops->interrupt, ++ ctx->ops->irq_thread, 0, SST_DRV_NAME, ++ ctx); ++ if (ret) ++ return ret; ++ pr_debug("Registered IRQ %#x\n", ctx->irq_num); ++ ++ /*Register LPE Control as misc driver*/ ++ ret = misc_register(&lpe_ctrl); ++ if (ret) { ++ pr_err("couldn't register control device\n"); ++ goto do_free_wq; ++ } ++ /* mask all SSP and DMA irq to IA - enabled in acpi kernel driver */ ++ sst_shim_write64(ctx->shim, SST_IMRX, 0xFFFF0038); ++ ++ if (ctx->use_32bit_ops) { ++ pr_debug("allocate mem for context save/restore\n "); ++ /*allocate mem for fw context save during suspend*/ ++ ctx->fw_cntx = devm_kzalloc(ctx->dev, FW_CONTEXT_MEM, GFP_KERNEL); ++ if (!ctx->fw_cntx) { ++ ret = -ENOMEM; ++ goto do_free_misc; ++ } ++ /*setting zero as that is valid mem to restore*/ ++ ctx->fw_cntx_size = 0; ++ } ++ ++ platform_set_drvdata(pdev, ctx); ++ pm_runtime_set_active(dev); ++ pm_runtime_enable(dev); ++ register_sst(dev); ++ sst_debugfs_init(ctx); ++ sst_set_fw_state_locked(ctx, SST_UN_INIT); ++ sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64); ++ pr_info("%s successfully done!\n", __func__); ++ return ret; ++ ++do_free_misc: ++ misc_deregister(&lpe_ctrl); ++do_free_wq: ++ sst_destroy_workqueue(ctx); ++ ++ sst_drv_ctx = NULL; ++ platform_set_drvdata(pdev, NULL); ++ pr_err("%s: failed with %d\n", __func__, ret); ++ return ret; ++} ++ ++/** ++* intel_sst_remove - remove function ++* ++* @pdev: platform device structure ++* ++* This function is called by OS when a device is unloaded ++* This frees the interrupt etc ++*/ ++int sst_acpi_remove(struct platform_device *pdev) ++{ ++ struct intel_sst_drv *ctx; ++ ++ ctx = platform_get_drvdata(pdev); ++ sst_debugfs_exit(ctx); ++ pm_runtime_get_noresume(ctx->dev); ++ pm_runtime_disable(ctx->dev); ++ unregister_sst(ctx->dev); ++ sst_set_fw_state_locked(ctx, SST_UN_INIT); ++ misc_deregister(&lpe_ctrl); ++ kfree(ctx->runtime_param.param.addr); ++ flush_scheduled_work(); ++ sst_destroy_workqueue(ctx); ++ kfree(ctx->fw_sg_list.src); ++ kfree(ctx->fw_sg_list.dst); ++ ctx->fw_sg_list.list_len = 0; ++ kfree(ctx->fw_in_mem); ++ ctx->fw_in_mem = NULL; ++ sst_memcpy_free_resources(); ++ sst_drv_ctx = NULL; ++ platform_set_drvdata(pdev, NULL); ++ return 0; ++} ++ ++#else ++int sst_acpi_probe(struct platform_device *pdev) ++{ ++ return -EINVAL; ++} ++ ++int sst_acpi_remove(struct platform_device *pdev) ++{ ++ return -EINVAL; ++} ++#endif ++ ++MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine ACPI Driver"); ++MODULE_AUTHOR("Ramesh Babu K V"); ++MODULE_AUTHOR("Omair Mohammed Abdullah"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("sst"); +diff --git a/sound/soc/intel/sst/sst_app_compat_interface.c b/sound/soc/intel/sst/sst_app_compat_interface.c +new file mode 100644 +index 0000000..4babd76 +--- /dev/null ++++ b/sound/soc/intel/sst/sst_app_compat_interface.c +@@ -0,0 +1,85 @@ ++ ++/* ++ * sst_app_compat_interface.c - Intel SST Driver for audio engine ++ * ++ * Copyright (C) 2013-14 Intel Corp ++ * Authors: Subhransu S. Prusty ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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. ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * This driver exposes the audio engine functionalities to the ALSA ++ * and middleware. ++ */ ++ ++/* This file is included from sst.c */ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include "sst.h" ++ ++struct snd_ppp_params32 { ++ __u8 algo_id;/* Post/Pre processing algorithm ID */ ++ __u8 str_id; /*Only 5 bits used 0 - 31 are valid*/ ++ __u8 enable; /* 0= disable, 1= enable*/ ++ __u8 operation; ++ __u32 size; /*Size of parameters for all blocks*/ ++ __u32 params; ++} __packed; ++ ++enum { ++SNDRV_SST_SET_ALGO32 = _IOW('L', 0x30, struct snd_ppp_params32), ++SNDRV_SST_GET_ALGO32 = _IOWR('L', 0x31, struct snd_ppp_params32), ++}; ++ ++static long sst_algo_compat(unsigned int cmd, ++ struct snd_ppp_params32 __user *arg32) ++{ ++ int retval = 0; ++ struct snd_ppp_params32 algo_params32; ++ struct snd_ppp_params algo_params; ++ ++ if (copy_from_user(&algo_params32, arg32, sizeof(algo_params32))) { ++ pr_debug("%s: copy from user failed: %d\n", __func__, retval); ++ return -EINVAL; ++ } ++ ++ memcpy(&algo_params, &algo_params32, sizeof(algo_params32)-sizeof(__u32)); ++ algo_params.params = compat_ptr(algo_params32.params); ++ retval = intel_sst_ioctl_dsp(cmd, &algo_params, (unsigned long)arg32); ++ return retval; ++} ++ ++static long intel_sst_ioctl_compat(struct file *file_ptr, ++ unsigned int cmd, unsigned long arg) ++{ ++ void __user *argp = compat_ptr(arg); ++ ++ switch (_IOC_NR(cmd)) { ++ case _IOC_NR(SNDRV_SST_DRIVER_INFO): ++ case _IOC_NR(SNDRV_SST_TUNING_PARAMS): ++ return intel_sst_ioctl(file_ptr, cmd, (unsigned long)argp); ++ case _IOC_NR(SNDRV_SST_SET_ALGO32): ++ return sst_algo_compat(SNDRV_SST_SET_ALGO, argp); ++ case _IOC_NR(SNDRV_SST_GET_ALGO32): ++ return sst_algo_compat(SNDRV_SST_GET_ALGO, argp); ++ ++ default: ++ return -ENOTTY; ++ } ++ return 0; ++} +diff --git a/sound/soc/intel/sst/sst_app_interface.c b/sound/soc/intel/sst/sst_app_interface.c +new file mode 100644 +index 0000000..cfbbf4f +--- /dev/null ++++ b/sound/soc/intel/sst/sst_app_interface.c +@@ -0,0 +1,342 @@ ++/* ++ * sst_app_interface.c - Intel SST Driver for audio engine ++ * ++ * Copyright (C) 2008-10 Intel Corp ++ * Authors: Vinod Koul ++ * Harsha Priya ++ * Dharageswari R ++ * Jeeja KP ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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. ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * This driver exposes the audio engine functionalities to the ALSA ++ * and middleware. ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "../sst_platform.h" ++#include "../platform_ipc_v2.h" ++#include "sst.h" ++ ++#define AM_MODULE 1 ++ ++/** ++ * intel_sst_open_cntrl - opens a handle to driver ++ * ++ * @i_node: inode structure ++ * @file_ptr:pointer to file ++ * ++ * This function is called by OS when a user space component ++ * tries to get a driver handle to /dev/intel_sst_control. ++ * Only one handle at a time will be allowed ++ * This is for control operations only ++ */ ++int intel_sst_open_cntrl(struct inode *i_node, struct file *file_ptr) ++{ ++ unsigned int retval; ++ ++ /* audio manager open */ ++ mutex_lock(&sst_drv_ctx->stream_lock); ++ retval = intel_sst_check_device(); ++ if (retval) { ++ mutex_unlock(&sst_drv_ctx->stream_lock); ++ return retval; ++ } ++ pr_debug("AM handle opened\n"); ++ ++ mutex_unlock(&sst_drv_ctx->stream_lock); ++ return retval; ++} ++ ++ ++int intel_sst_release_cntrl(struct inode *i_node, struct file *file_ptr) ++{ ++ /* audio manager close */ ++ mutex_lock(&sst_drv_ctx->stream_lock); ++ sst_pm_runtime_put(sst_drv_ctx); ++ mutex_unlock(&sst_drv_ctx->stream_lock); ++ pr_debug("AM handle closed\n"); ++ return 0; ++} ++ ++/** ++ * sst_get_max_streams - Function to populate the drv info structure ++ * with the max streams ++ * @info: the out params that holds the drv info ++ * ++ * This function is called when max streams count is required ++**/ ++void sst_get_max_streams(struct snd_sst_driver_info *info) ++{ ++ pr_debug("info.max_streams %d num_probes %d\n", sst_drv_ctx->info.max_streams, ++ sst_drv_ctx->info.num_probes); ++ info->max_streams = sst_drv_ctx->info.max_streams - sst_drv_ctx->info.num_probes; ++} ++ ++/** ++ * sst_create_algo_ipc - create ipc msg for algorithm parameters ++ * ++ * @algo_params: Algorithm parameters ++ * @msg: post msg pointer ++ * @pvt_id: Checked by wake_up_block ++ * ++ * This function is called to create ipc msg ++ * For copying the mailbox data the function returns offset in bytes to mailbox ++ * memory where the mailbox data should be copied after msg header ++ */ ++static int sst_create_algo_ipc(struct snd_ppp_params *algo_params, ++ struct ipc_post **msg, int pvt_id) ++{ ++ u32 header_size = 0; ++ u32 ipc_msg_size = sizeof(u32) + sizeof(*algo_params) ++ - sizeof(algo_params->params) + algo_params->size; ++ u32 offset = 0; ++ ++ if (ipc_msg_size > SST_MAILBOX_SIZE) ++ return -ENOMEM; ++ if (sst_create_ipc_msg(msg, true)) ++ return -ENOMEM; ++ sst_fill_header(&(*msg)->header, ++ IPC_IA_ALG_PARAMS, 1, pvt_id); ++ (*msg)->header.part.data = ipc_msg_size; ++ memcpy((*msg)->mailbox_data, &(*msg)->header, sizeof(u32)); ++ offset = sizeof(u32); ++ header_size = sizeof(*algo_params) - sizeof(algo_params->params); ++ memcpy((*msg)->mailbox_data + offset, algo_params, header_size); ++ offset += header_size; ++ return offset; ++} ++ ++static long sst_send_algo(struct snd_ppp_params *algo_params, ++ struct sst_block *block, enum sst_algo_ops algo) ++{ ++ struct ipc_post *msg; ++ int retval; ++ int offset; ++ ++ pr_debug("Algo ID %d Str id %d Enable %d Size %d\n", ++ algo_params->algo_id, algo_params->str_id, ++ algo_params->enable, algo_params->size); ++ ++ algo_params->operation = algo; ++ ++ offset = sst_create_algo_ipc(algo_params, &msg, block->drv_id); ++ if (offset < 0) ++ return offset; ++ ++ if (copy_from_user(msg->mailbox_data + offset, ++ algo_params->params, algo_params->size)) { ++ kfree(msg); ++ return -EFAULT; ++ } ++ ++ sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); ++ retval = sst_wait_timeout(sst_drv_ctx, block); ++ if (retval) { ++ pr_debug("%s: failed for algo ops %s with retval %d\n", ++ __func__, algo ? "SST_GET_ALGO" : "SST_SET_ALGO", retval); ++ return -EIO; ++ } ++ return 0; ++} ++ ++/** ++ * intel_sst_ioctl_dsp - receives the device ioctl's ++ * ++ * @cmd:Ioctl cmd ++ * @arg:data ++ * ++ * This function is called when a user space component ++ * sends a DSP Ioctl to SST driver ++ */ ++long intel_sst_ioctl_dsp(unsigned int cmd, ++ struct snd_ppp_params *algo_params, unsigned long arg) ++{ ++ int retval = 0; ++ struct snd_ppp_params *algo_params_copied; ++ struct sst_block *block; ++ int pvt_id; ++ ++ pvt_id = sst_assign_pvt_id(sst_drv_ctx); ++ block = sst_create_block(sst_drv_ctx, IPC_IA_ALG_PARAMS, pvt_id); ++ if (block == NULL) ++ return -ENOMEM; ++ ++ switch (_IOC_NR(cmd)) { ++ case _IOC_NR(SNDRV_SST_SET_ALGO): ++ retval = sst_send_algo(algo_params, block, SST_SET_ALGO); ++ break; ++ ++ case _IOC_NR(SNDRV_SST_GET_ALGO): ++ retval = sst_send_algo(algo_params, block, SST_GET_ALGO); ++ if (retval) ++ break; ++ algo_params_copied = (struct snd_ppp_params *)block->data; ++ ++ if (algo_params_copied->size > algo_params->size) { ++ pr_debug("mem insufficient to copy\n"); ++ retval = -EMSGSIZE; ++ break; ++ } else { ++ char __user *tmp; ++ struct snd_ppp_params *get_params; ++ char *pp; ++ ++ tmp = (char __user *)arg + offsetof( ++ struct snd_ppp_params, size); ++ if (copy_to_user(tmp, &algo_params_copied->size, ++ sizeof(u32))) { ++ retval = -EFAULT; ++ break; ++ } ++ tmp = (char __user *)arg + offsetof( ++ struct snd_ppp_params, enable); ++ if (copy_to_user(tmp, &algo_params_copied->enable, ++ sizeof(u8))) { ++ retval = -EFAULT; ++ break; ++ } ++ if (algo_params_copied->size == 0) ++ break; ++ ++ get_params = kmalloc(sizeof(*get_params), GFP_KERNEL); ++ if (!get_params) { ++ pr_err("sst: mem alloc failed\n"); ++ break; ++ } ++ memcpy(get_params, algo_params_copied, ++ sizeof(*get_params)); ++ ++ get_params->params = kmalloc(get_params->size, GFP_KERNEL); ++ if (!get_params->params) { ++ pr_err("sst: mem alloc failed\n"); ++ goto free_mem; ++ } ++ pp = (char *)algo_params_copied; ++ pp = pp + sizeof(*get_params) - ++ sizeof(get_params->params); ++ memcpy(get_params->params, pp, get_params->size); ++ if (copy_to_user(algo_params->params, ++ get_params->params, ++ get_params->size)) { ++ retval = -EFAULT; ++ } ++ kfree(get_params->params); ++ ++free_mem: ++ kfree(get_params); ++ ++ } ++ break; ++ } ++ sst_free_block(sst_drv_ctx, block); ++ pr_debug("ioctl dsp return = %d, for cmd = %x\n", retval, cmd); ++ return retval; ++} ++ ++static long sst_ioctl_tuning_params(unsigned int cmd, unsigned long arg) ++{ ++ struct snd_sst_tuning_params params; ++ struct ipc_post *msg; ++ unsigned long address; ++ ++ if (copy_from_user(¶ms, (void __user *)arg, sizeof(params))) ++ return -EFAULT; ++ pr_debug("sst: Parameter %d, Stream %d, Size %d\n", params.type, ++ params.str_id, params.size); ++ if (sst_create_ipc_msg(&msg, true)) ++ return -ENOMEM; ++ address = (unsigned long)params.addr; ++ ++ switch (_IOC_NR(cmd)) { ++ case _IOC_NR(SNDRV_SST_TUNING_PARAMS): ++ sst_fill_header(&msg->header, IPC_IA_TUNING_PARAMS, 1, ++ params.str_id); ++ break; ++ } ++ msg->header.part.data = sizeof(u32) + sizeof(params) + params.size; ++ memcpy(msg->mailbox_data, &msg->header.full, sizeof(u32)); ++ memcpy(msg->mailbox_data + sizeof(u32), ¶ms, sizeof(params)); ++ /* driver doesn't need to send address, so overwrite addr with data */ ++ if (copy_from_user(msg->mailbox_data + sizeof(u32) ++ + sizeof(params) - sizeof(params.addr), ++ (void __user *)address, params.size)) { ++ kfree(msg->mailbox_data); ++ kfree(msg); ++ return -EFAULT; ++ } ++ ++ sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); ++ return 0; ++} ++/** ++ * intel_sst_ioctl - receives the device ioctl's ++ * @file_ptr:pointer to file ++ * @cmd:Ioctl cmd ++ * @arg:data ++ * ++ * This function is called by OS when a user space component ++ * sends an Ioctl to SST driver ++ */ ++long intel_sst_ioctl(struct file *file_ptr, unsigned int cmd, unsigned long arg) ++{ ++ int retval = 0; ++ struct snd_ppp_params algo_params; ++ ++ if (sst_drv_ctx->sst_state != SST_FW_RUNNING) ++ return -EBUSY; ++ ++ switch (_IOC_NR(cmd)) { ++ case _IOC_NR(SNDRV_SST_DRIVER_INFO): { ++ struct snd_sst_driver_info info; ++ ++ pr_debug("SNDRV_SST_DRIVER_INFO received\n"); ++ sst_get_max_streams(&info); ++ ++ if (copy_to_user((void __user *)arg, &info, ++ sizeof(info))) ++ retval = -EFAULT; ++ break; ++ } ++ case _IOC_NR(SNDRV_SST_GET_ALGO): ++ case _IOC_NR(SNDRV_SST_SET_ALGO): ++ if (copy_from_user(&algo_params, (void __user *)arg, ++ sizeof(algo_params))) { ++ return -EFAULT; ++ } ++ retval = intel_sst_ioctl_dsp(cmd, &algo_params, arg); ++ break; ++ ++ case _IOC_NR(SNDRV_SST_TUNING_PARAMS): ++ retval = sst_ioctl_tuning_params(cmd, arg); ++ break; ++ ++ default: ++ retval = -EINVAL; ++ } ++ pr_debug("intel_sst_ioctl:complete ret code = %d for command = %x\n", retval, cmd); ++ return retval; ++} ++ +diff --git a/sound/soc/intel/sst/sst_debug.c b/sound/soc/intel/sst/sst_debug.c +new file mode 100644 +index 0000000..ce771c5 +--- /dev/null ++++ b/sound/soc/intel/sst/sst_debug.c +@@ -0,0 +1,1328 @@ ++/* ++ * sst_debug.c - Intel SST Driver debugfs support ++ * ++ * Copyright (C) 2012 Intel Corp ++ * Authors: Vinod Koul ++ * Omair Mohammed Abdullah ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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. ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * This file contains all debugfs functions ++ * Support includes: ++ * - Disabling/Enabling runtime PM for SST ++ * - Reading/Writing SST SHIM registers ++ * - Reading/Enabling Input OSC Clock ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": debugfs: " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "../sst_platform.h" ++#include "../platform_ipc_v2.h" ++#include "sst.h" ++ ++#define DMA_NUM_CH 8 ++#define DEBUGFS_SSP_BUF_SIZE 300 /* 22 chars * 12 reg*/ ++#define DEBUGFS_DMA_BUF_SIZE 2500 /* 32 chars * 78 regs*/ ++ ++/* Register Offsets of SSP3 and LPE DMA */ ++u32 ssp_reg_off[] = {0x0, 0x4, 0x8, 0xC, 0x10, 0x28, 0x2C, 0x30, 0x34, 0x38, ++ 0x3C, 0x40}; ++/* Excludes the channel registers */ ++u32 dma_reg_off[] = {0x2C0, 0x2C8, 0x2D0, 0x2D8, 0x2E0, 0x2E8, ++ 0x2F0, 0x2F8, 0x300, 0x308, 0x310, 0x318, 0x320, 0x328, 0x330, ++ 0x338, 0x340, 0x348, 0x350, 0x358, 0x360, 0x368, 0x370, 0x378, ++ 0x380, 0x388, 0x390, 0x398, 0x3A0, 0x3A8, 0x3B0, 0x3C8, 0x3D0, ++ 0x3D8, 0x3E0, 0x3E8, 0x3F0, 0x3F8}; ++ ++static ssize_t sst_debug_shim_read(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct intel_sst_drv *drv = file->private_data; ++ unsigned long long val = 0; ++ unsigned int addr; ++ char buf[512]; ++ char name[8]; ++ int pos = 0; ++ ++ buf[0] = 0; ++ if (drv->sst_state == SST_SUSPENDED) { ++ pr_err("FW suspended, cannot read SHIM registers\n"); ++ return -EFAULT; ++ } ++ ++ for (addr = SST_SHIM_BEGIN; addr <= SST_SHIM_END; addr += 8) { ++ switch (drv->pci_id) { ++ case SST_CLV_PCI_ID: ++ val = sst_shim_read(drv->shim, addr); ++ break; ++ case SST_MRFLD_PCI_ID: ++ case SST_BYT_PCI_ID: ++ case SST_CHT_PCI_ID: ++ val = sst_shim_read64(drv->shim, addr); ++ break; ++ } ++ ++ name[0] = 0; ++ switch (addr) { ++ case SST_ISRX: ++ strcpy(name, "ISRX"); break; ++ case SST_ISRD: ++ strcpy(name, "ISRD"); break; ++ case SST_IPCX: ++ strcpy(name, "IPCX"); break; ++ case SST_IPCD: ++ strcpy(name, "IPCD"); break; ++ case SST_IMRX: ++ strcpy(name, "IMRX"); break; ++ case SST_IMRD: ++ strcpy(name, "IMRD"); break; ++ } ++ pos += sprintf(buf + pos, "0x%.2x: %.8llx %s\n", addr, val, name); ++ } ++ ++ return simple_read_from_buffer(user_buf, count, ppos, ++ buf, strlen(buf)); ++} ++ ++static ssize_t sst_debug_shim_write(struct file *file, ++ const char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct intel_sst_drv *drv = file->private_data; ++ char buf[32]; ++ char *start = buf, *end; ++ unsigned long long value; ++ unsigned long reg_addr; ++ int ret_val; ++ size_t buf_size = min(count, sizeof(buf)-1); ++ ++ if (copy_from_user(buf, user_buf, buf_size)) ++ return -EFAULT; ++ buf[buf_size] = 0; ++ ++ if (drv->sst_state == SST_SUSPENDED) { ++ pr_err("FW suspended, cannot write SHIM registers\n"); ++ return -EFAULT; ++ } ++ ++ while (*start == ' ') ++ start++; ++ end = start; ++ while (isalnum(*end)) ++ end++; ++ *end = 0; ++ ++ ret_val = kstrtoul(start, 16, ®_addr); ++ if (ret_val) { ++ pr_err("kstrtoul failed, ret_val = %d\n", ret_val); ++ return ret_val; ++ } ++ if (!(SST_SHIM_BEGIN < reg_addr && reg_addr < SST_SHIM_END)) { ++ pr_err("invalid shim address: 0x%lx\n", reg_addr); ++ return -EINVAL; ++ } ++ ++ start = end + 1; ++ while (*start == ' ') ++ start++; ++ ++ ret_val = kstrtoull(start, 16, &value); ++ if (ret_val) { ++ pr_err("kstrtoul failed, ret_val = %d\n", ret_val); ++ return ret_val; ++ } ++ ++ pr_debug("writing shim: 0x%.2lx=0x%.8llx", reg_addr, value); ++ ++ if (drv->pci_id == SST_CLV_PCI_ID) ++ sst_shim_write(drv->shim, reg_addr, (u32) value); ++ else if (drv->pci_id == SST_MRFLD_PCI_ID) ++ sst_shim_write64(drv->shim, reg_addr, (u64) value); ++ ++ /* Userspace has been fiddling around behind the kernel's back */ ++ add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); ++ return buf_size; ++} ++ ++static const struct file_operations sst_debug_shim_ops = { ++ .open = simple_open, ++ .read = sst_debug_shim_read, ++ .write = sst_debug_shim_write, ++ .llseek = default_llseek, ++}; ++ ++#define RESVD_DUMP_SZ 40 ++#define IA_LPE_MAILBOX_DUMP_SZ 100 ++#define LPE_IA_MAILBOX_DUMP_SZ 100 ++#define SCU_LPE_MAILBOX_DUMP_SZ 256 ++#define LPE_SCU_MAILBOX_DUMP_SZ 256 ++ ++static inline int is_fw_running(struct intel_sst_drv *drv) ++{ ++ pm_runtime_get_sync(drv->dev); ++ atomic_inc(&drv->pm_usage_count); ++ if (drv->sst_state != SST_FW_RUNNING) { ++ pr_err("FW not running\n"); ++ sst_pm_runtime_put(drv); ++ return -EFAULT; ++ } ++ return 0; ++} ++ ++static inline int read_buffer_fromio(char *dest, unsigned int sz, ++ const u32 __iomem *from, ++ unsigned int num_dwords) ++{ ++ int i; ++ const unsigned int rowsz = 16, groupsz = 4; ++ const unsigned int size = num_dwords * sizeof(u32); ++ unsigned int linelen, printed = 0, remaining = size; ++ ++ u8 *tmp = kmalloc(size, GFP_KERNEL); ++ if (!tmp) ++ return -ENOMEM; ++ memcpy_fromio(tmp, from, size); ++ for (i = 0; i < size; i += rowsz) { ++ linelen = min(remaining, rowsz); ++ remaining -= rowsz; ++ hex_dump_to_buffer(tmp + i, linelen, rowsz, groupsz, ++ dest + printed, sz - printed, false); ++ printed += linelen * 2 + linelen / groupsz - 1; ++ *(dest + printed++) = '\n'; ++ *(dest + printed) = 0; ++ } ++ kfree(tmp); ++ return 0; ++} ++ ++static inline int copy_sram_to_user_buffer(char __user *user_buf, size_t count, loff_t *ppos, ++ unsigned int num_dwords, const u32 __iomem *from, ++ u32 offset) ++{ ++ ssize_t bytes_read; ++ char *buf; ++ int pos; ++ unsigned int bufsz = 48 + sizeof(u32) * num_dwords * (2 + 1) + 1; ++ ++ buf = kmalloc(bufsz, GFP_KERNEL); ++ if (!buf) { ++ pr_err("%s: no memory\n", __func__); ++ return -ENOMEM; ++ } ++ *buf = 0; ++ pos = scnprintf(buf, 48, "Reading %u dwords from offset %#x\n", ++ num_dwords, offset); ++ read_buffer_fromio(buf + pos, bufsz - pos, from, num_dwords); ++ bytes_read = simple_read_from_buffer(user_buf, count, ppos, ++ buf, strlen(buf)); ++ kfree(buf); ++ return bytes_read; ++} ++ ++static ssize_t sst_debug_sram_lpe_debug_read(struct file *file, ++ char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ ++ struct intel_sst_drv *drv = file->private_data; ++ int ret = 0; ++ ++ ret = is_fw_running(drv); ++ if (ret) ++ return ret; ++ ++ ret = copy_sram_to_user_buffer(user_buf, count, ppos, RESVD_DUMP_SZ, ++ (u32 *)(drv->mailbox + SST_RESERVED_OFFSET), ++ SST_RESERVED_OFFSET); ++ sst_pm_runtime_put(drv); ++ return ret; ++} ++ ++static const struct file_operations sst_debug_sram_lpe_debug_ops = { ++ .open = simple_open, ++ .read = sst_debug_sram_lpe_debug_read, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t sst_debug_sram_lpe_checkpoint_read(struct file *file, ++ char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ ++ struct intel_sst_drv *drv = file->private_data; ++ int ret = 0; ++ u32 offset; ++ ++ ret = is_fw_running(drv); ++ if (ret) ++ return ret; ++ ++ offset = sst_drv_ctx->pdata->debugfs_data->checkpoint_offset; ++ ++ ret = copy_sram_to_user_buffer(user_buf, count, ppos, ++ sst_drv_ctx->pdata->debugfs_data->checkpoint_size, ++ (u32 *)(drv->mailbox + offset), offset); ++ sst_pm_runtime_put(drv); ++ return ret; ++} ++ ++static const struct file_operations sst_debug_sram_lpe_checkpoint_ops = { ++ .open = simple_open, ++ .read = sst_debug_sram_lpe_checkpoint_read, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t sst_debug_sram_ia_lpe_mbox_read(struct file *file, ++ char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ ++ struct intel_sst_drv *drv = file->private_data; ++ int ret = 0; ++ ++ ret = is_fw_running(drv); ++ if (ret) ++ return ret; ++ ret = copy_sram_to_user_buffer(user_buf, count, ppos, IA_LPE_MAILBOX_DUMP_SZ, ++ (u32 *)(drv->mailbox + SST_MAILBOX_SEND), ++ SST_MAILBOX_SEND); ++ sst_pm_runtime_put(drv); ++ return ret; ++} ++ ++static const struct file_operations sst_debug_sram_ia_lpe_mbox_ops = { ++ .open = simple_open, ++ .read = sst_debug_sram_ia_lpe_mbox_read, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t sst_debug_sram_lpe_ia_mbox_read(struct file *file, ++ char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ ++ struct intel_sst_drv *drv = file->private_data; ++ int ret = 0; ++ ++ ret = is_fw_running(drv); ++ if (ret) ++ return ret; ++ ++ ret = copy_sram_to_user_buffer(user_buf, count, ppos, LPE_IA_MAILBOX_DUMP_SZ, ++ (u32 *)(drv->mailbox + drv->mailbox_recv_offset), ++ drv->mailbox_recv_offset); ++ sst_pm_runtime_put(drv); ++ return ret; ++} ++ ++static const struct file_operations sst_debug_sram_lpe_ia_mbox_ops = { ++ .open = simple_open, ++ .read = sst_debug_sram_lpe_ia_mbox_read, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t sst_debug_sram_lpe_scu_mbox_read(struct file *file, ++ char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct intel_sst_drv *drv = file->private_data; ++ int ret = 0; ++ ++ ret = is_fw_running(drv); ++ if (ret) ++ return ret; ++ ret = copy_sram_to_user_buffer(user_buf, count, ppos, LPE_SCU_MAILBOX_DUMP_SZ, ++ (u32 *)(drv->mailbox + SST_LPE_SCU_MAILBOX), ++ SST_LPE_SCU_MAILBOX); ++ sst_pm_runtime_put(drv); ++ return ret; ++} ++ ++static const struct file_operations sst_debug_sram_lpe_scu_mbox_ops = { ++ .open = simple_open, ++ .read = sst_debug_sram_lpe_scu_mbox_read, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t sst_debug_sram_scu_lpe_mbox_read(struct file *file, ++ char __user *user_buf, size_t count, loff_t *ppos) ++{ struct intel_sst_drv *drv = file->private_data; ++ int ret = 0; ++ ++ ret = is_fw_running(drv); ++ if (ret) ++ return ret; ++ ret = copy_sram_to_user_buffer(user_buf, count, ppos, SCU_LPE_MAILBOX_DUMP_SZ, ++ (u32 *)(drv->mailbox + SST_SCU_LPE_MAILBOX), ++ SST_SCU_LPE_MAILBOX); ++ sst_pm_runtime_put(drv); ++ return ret; ++} ++ ++static const struct file_operations sst_debug_sram_scu_lpe_mbox_ops = { ++ .open = simple_open, ++ .read = sst_debug_sram_scu_lpe_mbox_read, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t sst_debug_lpe_log_enable_write(struct file *file, ++ const char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct intel_sst_drv *drv = file->private_data; ++ struct ipc_post *msg = NULL; ++ char buf[32]; ++ int str_id = 0; /* DUMMY, required by post message */ ++ struct snd_sst_lpe_log_params params; ++ int ret_val = 0; ++ char *start = buf, *end; ++ int i = 0; ++ u8 *addr; ++ unsigned long tmp; ++ ++ size_t buf_size = min(count, sizeof(buf)-1); ++ memset(¶ms, 0, sizeof(params)); ++ ++ ret_val = is_fw_running(drv); ++ if (ret_val) ++ return ret_val; ++ ++ if (copy_from_user(buf, user_buf, buf_size)) { ++ ret_val = -EFAULT; ++ goto put_pm_runtime; ++ } ++ ++ buf[buf_size] = 0; ++ ++ addr = ¶ms.dbg_type; ++ for (i = 0; i < (sizeof(params) - sizeof(u8)); i++) { ++ while (*start == ' ') ++ start++; ++ end = start; ++ while (isalnum(*end)) ++ end++; ++ *end = 0; ++ ret_val = kstrtoul(start, 16, &tmp); ++ if (ret_val) { ++ pr_err("kstrtoul failed, ret_val = %d\n", ret_val); ++ goto put_pm_runtime; ++ } ++ *addr++ = (u8)tmp; ++ start = end + 1; ++ } ++ ++ pr_debug("dbg_type = %d module_id = %d log_level = %d\n", ++ params.dbg_type, params.module_id, params.log_level); ++ ++ if (params.dbg_type < NO_DEBUG || params.dbg_type > PTI_DEBUG) { ++ ret_val = -EINVAL; ++ goto put_pm_runtime; ++ } ++ ++ ret_val = sst_create_ipc_msg(&msg, true); ++ if (ret_val != 0) ++ goto put_pm_runtime; ++ ++ if (sst_drv_ctx->pci_id != SST_MRFLD_PCI_ID) { ++ sst_fill_header(&msg->header, IPC_IA_DBG_LOG_ENABLE, 1, ++ str_id); ++ msg->header.part.data = sizeof(u32) + sizeof(params); ++ memcpy(msg->mailbox_data, &msg->header.full, sizeof(u32)); ++ memcpy(msg->mailbox_data + sizeof(u32), ¶ms, ++ sizeof(params)); ++ } ++ drv->ops->sync_post_message(msg); ++ ret_val = buf_size; ++put_pm_runtime: ++ sst_pm_runtime_put(drv); ++ return ret_val; ++} ++ ++/* ++ * Circular buffer hdr -> 0x1000 ++ * log data starts at 0x1010 ++ */ ++static ssize_t sst_debug_lpe_log_enable_read(struct file *file, ++ char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct intel_sst_drv *drv = file->private_data; ++ struct lpe_log_buf_hdr buf_hdr; ++ size_t size1, size2, offset, bytes_read; ++ char *buf = NULL; ++ int ret; ++ ++ ret = is_fw_running(drv); ++ if (ret) ++ return ret; ++ ++ /* Get the sram lpe log buffer header */ ++ memcpy_fromio(&buf_hdr, (u32 *)(drv->mailbox + SST_SCU_LPE_MAILBOX), ++ sizeof(buf_hdr)); ++ if (buf_hdr.rd_addr == buf_hdr.wr_addr) { ++ pr_err("SRAM emptry\n"); ++ ret = -ENODATA; ++ goto put_pm_runtime; ++ } else if (buf_hdr.rd_addr < buf_hdr.wr_addr) { ++ size1 = buf_hdr.wr_addr - buf_hdr.rd_addr; ++ offset = (buf_hdr.rd_addr - buf_hdr.base_addr) ++ + SST_SCU_LPE_LOG_BUF; ++ pr_debug("Size = %zu, offset = %zx\n", size1, offset); ++ buf = vmalloc(size1); ++ if (buf == NULL) { ++ pr_err("Not enough memory to allocate\n"); ++ ret = -ENOMEM; ++ goto put_pm_runtime; ++ } ++ memcpy_fromio(buf, (u32 *)(drv->mailbox + offset), size1); ++ bytes_read = simple_read_from_buffer(user_buf, count, ppos, ++ buf, size1); ++ ++ buf_hdr.rd_addr = buf_hdr.rd_addr + bytes_read; ++ ++ } else { ++ /* Read including the end address as well */ ++ size1 = buf_hdr.end_addr - buf_hdr.rd_addr + 1; ++ offset = (buf_hdr.rd_addr - buf_hdr.base_addr) ++ + SST_SCU_LPE_LOG_BUF; ++ pr_debug("Size = %zu, offset = %zx\n", size1, offset); ++ buf = vmalloc(size1); ++ if (buf == NULL) { ++ pr_err("Not enough memory to allocate\n"); ++ ret = -ENOMEM; ++ goto put_pm_runtime; ++ } ++ memcpy_fromio(buf, (u32 *)(drv->mailbox + offset), size1); ++ bytes_read = simple_read_from_buffer(user_buf, count, ppos, ++ buf, size1); ++ if (bytes_read != size1) { ++ buf_hdr.rd_addr = buf_hdr.rd_addr + bytes_read; ++ goto update_rd_ptr; ++ } ++ ++ /* Wrap around lpe log buffer here */ ++ vfree(buf); ++ buf = NULL; ++ size2 = (buf_hdr.wr_addr - buf_hdr.base_addr); ++ offset = SST_SCU_LPE_LOG_BUF; ++ pr_debug("Size = %zu, offset = %zx\n", size2, offset); ++ buf = vmalloc(size2); ++ if (buf == NULL) { ++ pr_err("Not enough memory to allocate\n"); ++ ret = -ENOMEM; ++ goto put_pm_runtime; ++ } ++ memcpy_fromio(buf, (u32 *)(drv->mailbox + offset), size2); ++ bytes_read += simple_read_from_buffer(user_buf, ++ (count - bytes_read), ppos, buf, size2); ++ buf_hdr.rd_addr = buf_hdr.base_addr + bytes_read - size1; ++ ++ } ++update_rd_ptr: ++ if (bytes_read != 0) { ++ memcpy_toio((u32 *)(drv->mailbox + SST_SCU_LPE_MAILBOX + ++ 2 * sizeof(u32)), &(buf_hdr.rd_addr), sizeof(u32)); ++ pr_debug("read pointer restored\n"); ++ } ++ vfree(buf); ++ buf = NULL; ++ ret = bytes_read; ++put_pm_runtime: ++ sst_pm_runtime_put(drv); ++ return ret; ++} ++ ++static const struct file_operations sst_debug_lpe_log_enable_ops = { ++ .open = simple_open, ++ .write = sst_debug_lpe_log_enable_write, ++ .read = sst_debug_lpe_log_enable_read, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t sst_debug_rtpm_read(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct intel_sst_drv *drv = file->private_data; ++ char *status; ++ ++ int usage = atomic_read(&drv->pm_usage_count); ++ ++ pr_debug("RTPM usage: %d\n", usage); ++ status = drv->debugfs.runtime_pm_status ? "enabled\n" : "disabled\n"; ++ return simple_read_from_buffer(user_buf, count, ppos, ++ status, strlen(status)); ++} ++ ++static ssize_t sst_debug_rtpm_write(struct file *file, ++ const char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct intel_sst_drv *drv = file->private_data; ++ char buf[16]; ++ int sz = min(count, sizeof(buf)-1); ++ ++ int usage = atomic_read(&drv->pm_usage_count); ++ ++ pr_debug("RTPM Usage: %d\n", usage); ++ if (copy_from_user(buf, user_buf, sz)) ++ return -EFAULT; ++ buf[sz] = 0; ++ ++ if (!strncmp(buf, "enable\n", sz)) { ++ /* already enabled? */ ++ if (drv->debugfs.runtime_pm_status) ++ return -EINVAL; ++ drv->debugfs.runtime_pm_status = 1; ++ pm_runtime_allow(drv->dev); ++ sz = 6; /* strlen("enable") */ ++ } else if (!strncmp(buf, "disable\n", sz)) { ++ if (!drv->debugfs.runtime_pm_status) ++ return -EINVAL; ++ drv->debugfs.runtime_pm_status = 0; ++ pm_runtime_forbid(drv->dev); ++ sz = 7; /* strlen("disable") */ ++ } else ++ return -EINVAL; ++ return sz; ++} ++ ++static const struct file_operations sst_debug_rtpm_ops = { ++ .open = simple_open, ++ .read = sst_debug_rtpm_read, ++ .write = sst_debug_rtpm_write, ++ .llseek = default_llseek, ++}; ++ ++ ++static ssize_t sst_debug_readme_read(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ const char *buf = ++ "\nAll files can be read using 'cat'\n" ++ "1. 'echo disable > runtime_pm' disables runtime PM and will prevent SST from suspending.\n" ++ "To enable runtime PM, echo 'enable' to runtime_pm. Dmesg will print the runtime pm usage\n" ++ "if logs are enabled.\n" ++ "2. Write to shim register using 'echo > shim_dump'.\n" ++ "Valid address range is between 0x00 to 0x80 in increments of 8.\n" ++ "3. echo 1 > fw_clear_context , This sets the flag to skip the context restore\n" ++ "4. echo 1 > fw_clear_cache , This sets the flag to clear the cached copy of firmware\n" ++ "5. echo 1 > fw_reset_state ,This sets the fw state to uninit\n" ++ "6. echo memcpy > fw_dwnld_mode, This will set the firmware download mode to memcpy\n" ++ " echo lli > fw_dwnld_mode, This will set the firmware download mode to\n" ++ "dma lli mode\n" ++ " echo dma > fw_dwnld_mode, This will set the firmware download mode to\n" ++ "dma single block mode\n" ++ "7. iram_dump, dram_dump, interfaces provide mmap support to\n" ++ "get the iram and dram dump, these buffers will have data only\n" ++ "after the recovery is triggered\n"; ++ ++ const char *ctp_buf = ++ "8. Enable input clock by 'echo enable > osc_clk0'.\n" ++ "This prevents the input OSC clock from switching off till it is disabled by\n" ++ "'echo disable > osc_clk0'. The status of the clock indicated who are using it.\n" ++ "9. lpe_log_enable usage:\n" ++ " echo > lpe_log_enable.\n" ++ "10. cat fw_ssp_reg,This will dump the ssp register contents\n" ++ "11. cat fw_dma_reg,This will dump the dma register contents\n"; ++ ++ const char *mrfld_buf = ++ "8. lpe_log_enable usage:\n" ++ " echo > lpe_log_enable.\n" ++ "9. cat fw_ssp_reg,This will dump the ssp register contents\n" ++ "10. cat fw_dma_reg,This will dump the dma register contents\n" ++ "11. ddr_imr_dump interface provides mmap support to get the imr dump,\n" ++ "this buffer will have data only after the recovery is triggered\n" ++ "12. ipc usage:\n" ++ "\t ipc file works only in binary mode. The ipc format is .\n" ++ "\t drv_id in the ipc header will be overwritten with unique driver id in the driver\n"; ++ ++ char *readme = NULL; ++ const char *buf2 = NULL; ++ int size, ret = 0; ++ ++ switch (sst_drv_ctx->pci_id) { ++ case SST_CLV_PCI_ID: ++ size = strlen(buf) + strlen(ctp_buf) + 2; ++ buf2 = ctp_buf; ++ break; ++ case SST_MRFLD_PCI_ID: ++ size = strlen(buf) + strlen(mrfld_buf) + 2; ++ buf2 = mrfld_buf; ++ break; ++ default: ++ size = strlen(buf) + 1; ++ }; ++ ++ readme = kmalloc(size, GFP_KERNEL); ++ if (readme == NULL) { ++ pr_err("%s: no memory\n", __func__); ++ return -ENOMEM; ++ } ++ ++ if (buf2) ++ sprintf(readme, "%s%s\n", buf, buf2); ++ else ++ sprintf(readme, "%s\n", buf); ++ ++ ret = simple_read_from_buffer(user_buf, count, ppos, ++ readme, strlen(readme)); ++ kfree(readme); ++ return ret; ++} ++ ++static const struct file_operations sst_debug_readme_ops = { ++ .open = simple_open, ++ .read = sst_debug_readme_read, ++}; ++ ++static ssize_t sst_debug_osc_clk0_read(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ char status[16]; ++ int mode = -1; ++#ifdef CONFIG_INTEL_SCU_IPC_UTIL ++ mode = intel_scu_ipc_set_osc_clk0(0, CLK0_QUERY); ++#endif ++ ++ snprintf(status, 16, "0x%x\n", mode); ++ return simple_read_from_buffer(user_buf, count, ppos, ++ status, strlen(status)); ++} ++ ++static ssize_t sst_debug_osc_clk0_write(struct file *file, ++ const char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ char buf[16]; ++ int sz = min(count, sizeof(buf)-1); ++ ++ if (copy_from_user(buf, user_buf, sz)) ++ return -EFAULT; ++ buf[sz] = 0; ++ ++#ifdef CONFIG_INTEL_SCU_IPC_UTIL ++ if (!strncmp(buf, "enable\n", sz)) { ++ intel_scu_ipc_set_osc_clk0(true, CLK0_DEBUG); ++ sz = 6; /* strlen("enable") */ ++ } else if (!strncmp(buf, "disable\n", sz)) { ++ intel_scu_ipc_set_osc_clk0(false, CLK0_DEBUG); ++ sz = 7; /* strlen("disable") */ ++ } else ++ return -EINVAL; ++#endif ++ return sz; ++} ++ ++static const struct file_operations sst_debug_osc_clk0_ops = { ++ .open = simple_open, ++ .read = sst_debug_osc_clk0_read, ++ .write = sst_debug_osc_clk0_write, ++}; ++ ++static ssize_t sst_debug_fw_clear_cntx_read(struct file *file, ++ char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ char *status; ++ ++ status = atomic_read(&sst_drv_ctx->fw_clear_context) ? \ ++ "clear fw cntx\n" : "do not clear fw cntx\n"; ++ ++ return simple_read_from_buffer(user_buf, count, ppos, ++ status, strlen(status)); ++ ++} ++ ++static ssize_t sst_debug_fw_clear_cntx_write(struct file *file, ++ const char __user *user_buf, size_t count, loff_t *ppos) ++ ++{ ++ char buf[16]; ++ int sz = min(count, sizeof(buf)-1); ++ ++ if (copy_from_user(buf, user_buf, sz)) ++ return -EFAULT; ++ buf[sz] = 0; ++ ++ if (!strncmp(buf, "1\n", sz)) ++ atomic_set(&sst_drv_ctx->fw_clear_context, 1); ++ else ++ atomic_set(&sst_drv_ctx->fw_clear_context, 0); ++ ++ return sz; ++ ++} ++ ++static const struct file_operations sst_debug_fw_clear_cntx = { ++ .open = simple_open, ++ .read = sst_debug_fw_clear_cntx_read, ++ .write = sst_debug_fw_clear_cntx_write, ++}; ++ ++static ssize_t sst_debug_fw_clear_cache_read(struct file *file, ++ char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ char *status; ++ ++ status = atomic_read(&sst_drv_ctx->fw_clear_cache) ? \ ++ "cache clear flag set\n" : "cache clear flag not set\n"; ++ ++ return simple_read_from_buffer(user_buf, count, ppos, ++ status, strlen(status)); ++ ++} ++ ++static ssize_t sst_debug_fw_clear_cache_write(struct file *file, ++ const char __user *user_buf, size_t count, loff_t *ppos) ++ ++{ ++ char buf[16]; ++ int sz = min(count, sizeof(buf)-1); ++ ++ if (copy_from_user(buf, user_buf, sz)) ++ return -EFAULT; ++ buf[sz] = 0; ++ ++ if (!strncmp(buf, "1\n", sz)) ++ atomic_set(&sst_drv_ctx->fw_clear_cache, 1); ++ else ++ return -EINVAL; ++ ++ return sz; ++} ++ ++static const struct file_operations sst_debug_fw_clear_cache = { ++ .open = simple_open, ++ .read = sst_debug_fw_clear_cache_read, ++ .write = sst_debug_fw_clear_cache_write, ++}; ++ ++static ssize_t sst_debug_fw_reset_state_read(struct file *file, ++ char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ char state[16]; ++ ++ sprintf(state, "%d\n", sst_drv_ctx->sst_state); ++ ++ return simple_read_from_buffer(user_buf, count, ppos, ++ state, strlen(state)); ++ ++} ++ ++static ssize_t sst_debug_fw_reset_state_write(struct file *file, ++ const char __user *user_buf, size_t count, loff_t *ppos) ++ ++{ ++ char buf[16]; ++ int sz = min(count, sizeof(buf)-1); ++ ++ if (copy_from_user(buf, user_buf, sz)) ++ return -EFAULT; ++ buf[sz] = 0; ++ ++ if (!strncmp(buf, "1\n", sz)) ++ sst_set_fw_state_locked(sst_drv_ctx, SST_UN_INIT); ++ else ++ return -EINVAL; ++ ++ return sz; ++ ++} ++ ++static const struct file_operations sst_debug_fw_reset_state = { ++ .open = simple_open, ++ .read = sst_debug_fw_reset_state_read, ++ .write = sst_debug_fw_reset_state_write, ++}; ++ ++static ssize_t sst_debug_dwnld_mode_read(struct file *file, ++ char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ char *state = "error\n"; ++ ++ if (sst_drv_ctx->use_dma == 0) { ++ state = "memcpy\n"; ++ } else if (sst_drv_ctx->use_dma == 1) { ++ state = sst_drv_ctx->use_lli ? \ ++ "lli\n" : "dma\n"; ++ ++ } ++ ++ return simple_read_from_buffer(user_buf, count, ppos, ++ state, strlen(state)); ++ ++} ++ ++static ssize_t sst_debug_dwnld_mode_write(struct file *file, ++ const char __user *user_buf, size_t count, loff_t *ppos) ++ ++{ ++ char buf[16]; ++ int sz = min(count, sizeof(buf)-1); ++ ++ if (sst_drv_ctx->sst_state != SST_SUSPENDED && ++ sst_drv_ctx->sst_state != SST_UN_INIT) { ++ pr_err("FW should be in suspended/uninit state\n"); ++ return -EFAULT; ++ } ++ ++ if (copy_from_user(buf, user_buf, sz)) ++ return -EFAULT; ++ buf[sz] = '\0'; ++ ++ /* Firmware needs to be downloaded again to populate the lists */ ++ atomic_set(&sst_drv_ctx->fw_clear_cache, 1); ++ ++ if (!strncmp(buf, "memcpy\n", sz)) { ++ sst_drv_ctx->use_dma = 0; ++ } else if (!strncmp(buf, "lli\n", sz)) { ++ sst_drv_ctx->use_dma = 1; ++ sst_drv_ctx->use_lli = 1; ++ } else if (!strncmp(buf, "dma\n", sz)) { ++ sst_drv_ctx->use_dma = 1; ++ sst_drv_ctx->use_lli = 0; ++ } ++ return sz; ++ ++} ++ ++static const struct file_operations sst_debug_dwnld_mode = { ++ .open = simple_open, ++ .read = sst_debug_dwnld_mode_read, ++ .write = sst_debug_dwnld_mode_write, ++}; ++ ++static int dump_ssp_port(void __iomem *ssp_base, char *buf, int pos) ++{ ++ int index = 0; ++ ++ while (index < ARRAY_SIZE(ssp_reg_off)) { ++ pos += sprintf(buf + pos, "Reg: 0x%x: 0x%x\n", ssp_reg_off[index], ++ sst_reg_read(ssp_base, ssp_reg_off[index])); ++ index++; ++ } ++ return pos; ++} ++ ++static ssize_t sst_debug_ssp_reg_read(struct file *file, ++ char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ char *buf; ++ int i, pos = 0, off = 0; ++ struct intel_sst_drv *drv = file->private_data; ++ int num_ssp, buf_size, ret; ++ ++ num_ssp = sst_drv_ctx->pdata->debugfs_data->num_ssp; ++ buf_size = DEBUGFS_SSP_BUF_SIZE * num_ssp; ++ ++ buf = kmalloc(buf_size, GFP_KERNEL); ++ if (!buf) { ++ pr_err("%s: no memory\n", __func__); ++ return -ENOMEM; ++ } ++ ++ ret = is_fw_running(drv); ++ if (ret) ++ goto err; ++ ++ buf[0] = 0; ++ ++ for (i = 0; i < num_ssp ; i++) { ++ if (!sst_drv_ctx->debugfs.ssp[i]) { ++ pr_err("ssp %d port not mapped\n", i); ++ continue; ++ } ++ off = sst_drv_ctx->pdata->debugfs_data->ssp_reg_size * i; ++ pos = dump_ssp_port((sst_drv_ctx->debugfs.ssp[i]), buf, pos); ++ } ++ sst_pm_runtime_put(drv); ++ ++ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); ++err: ++ kfree(buf); ++ return ret; ++} ++ ++static const struct file_operations sst_debug_ssp_reg = { ++ .open = simple_open, ++ .read = sst_debug_ssp_reg_read, ++}; ++ ++static int dump_dma_reg(char *buf, int pos, int dma) ++{ ++ int i, index = 0; ++ int off = 0 ; ++ void __iomem *dma_reg; ++ ++ if (!sst_drv_ctx->debugfs.dma_reg[dma]) { ++ pr_err("dma %d not mapped\n", dma); ++ return pos; ++ } ++ ++ pos += sprintf(buf + pos, "\nDump DMA%d Reg\n\n", dma); ++ ++ dma_reg = sst_drv_ctx->debugfs.dma_reg[dma]; ++ ++ /* Dump the DMA channel registers */ ++ for (i = 0; i < DMA_NUM_CH; i++) { ++ pos += sprintf(buf + pos, "SAR%d: 0x%x: 0x%llx\n", i, off, ++ sst_reg_read64(dma_reg, off)); ++ off += 8; ++ ++ pos += sprintf(buf + pos, "DAR%d: 0x%x: 0x%llx\n", i, off, ++ sst_reg_read64(dma_reg, off)); ++ off += 8; ++ ++ pos += sprintf(buf + pos, "LLP%d: 0x%x: 0x%llx\n", i, off, ++ sst_reg_read64(dma_reg, off)); ++ off += 8; ++ ++ pos += sprintf(buf + pos, "CTL%d: 0x%x: 0x%llx\n", i, off, ++ sst_reg_read64(dma_reg, off)); ++ off += 0x28; ++ ++ pos += sprintf(buf + pos, "CFG%d: 0x%x: 0x%llx\n", i, off, ++ sst_reg_read64(dma_reg, off)); ++ off += 0x18; ++ } ++ ++ /* Dump the remaining DMA registers */ ++ while (index < ARRAY_SIZE(dma_reg_off)) { ++ pos += sprintf(buf + pos, "Reg: 0x%x: 0x%llx\n", dma_reg_off[index], ++ sst_reg_read64(dma_reg, dma_reg_off[index])); ++ index++; ++ } ++ return pos; ++} ++ ++static ssize_t sst_debug_dma_reg_read(struct file *file, ++ char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ char *buf; ++ int pos = 0; ++ int ret, i; ++ struct intel_sst_drv *drv = file->private_data; ++ int num_dma, buf_size; ++ ++ num_dma = sst_drv_ctx->pdata->debugfs_data->num_dma; ++ buf_size = DEBUGFS_DMA_BUF_SIZE * num_dma; ++ ++ buf = kmalloc(buf_size, GFP_KERNEL); ++ if (!buf) { ++ pr_err("%s: no memory\n", __func__); ++ return -ENOMEM; ++ } ++ ++ ret = is_fw_running(drv); ++ if (ret) ++ goto err; ++ ++ buf[0] = 0; ++ ++ for (i = 0; i < num_dma; i++) ++ pos = dump_dma_reg(buf, pos, i); ++ ++ sst_pm_runtime_put(drv); ++ ++ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); ++err: ++ kfree(buf); ++ return ret; ++} ++ ++static const struct file_operations sst_debug_dma_reg = { ++ .open = simple_open, ++ .read = sst_debug_dma_reg_read, ++}; ++ ++/** ++ * sst_debug_remap - function remaps the iram/dram buff to userspace ++ * ++ * @vma: vm_area_struct passed from userspace ++ * @buf: Physical addr of the pointer to be remapped ++ * @type: type of the buffer ++ * ++ * Remaps the kernel buffer to the userspace ++ */ ++static int sst_debug_remap(struct vm_area_struct *vma, char *buf, ++ enum sst_ram_type type) ++{ ++ int retval, length; ++ void *mem_area; ++ ++ if (!buf) ++ return -EIO; ++ ++ length = vma->vm_end - vma->vm_start; ++ pr_debug("iram length 0x%x\n", length); ++ ++ /* round it up to the page bondary */ ++ mem_area = (void *)PAGE_ALIGN((unsigned long)buf); ++ ++ /* map the whole physically contiguous area in one piece */ ++ retval = remap_pfn_range(vma, ++ vma->vm_start, ++ virt_to_phys((void *)mem_area) >> PAGE_SHIFT, ++ length, ++ vma->vm_page_prot); ++ if (retval) ++ pr_err("mapping failed %d ", retval); ++ return retval; ++} ++ ++int sst_debug_iram_dump_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ int retval; ++ struct intel_sst_drv *sst = sst_drv_ctx; ++ ++ retval = sst_debug_remap(vma, sst->dump_buf.iram_buf.buf, SST_IRAM); ++ ++ return retval; ++} ++ ++static const struct file_operations sst_debug_iram_dump = { ++ .open = simple_open, ++ .mmap = sst_debug_iram_dump_mmap, ++}; ++ ++int sst_debug_dram_dump_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ int retval; ++ struct intel_sst_drv *sst = sst_drv_ctx; ++ ++ retval = sst_debug_remap(vma, sst->dump_buf.dram_buf.buf, SST_DRAM); ++ ++ return retval; ++} ++ ++static const struct file_operations sst_debug_dram_dump = { ++ .open = simple_open, ++ .mmap = sst_debug_dram_dump_mmap, ++}; ++ ++int sst_debug_ddr_imr_dump_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ int retval; ++ struct intel_sst_drv *sst = sst_drv_ctx; ++ ++ retval = sst_debug_remap(vma, sst->ddr, 0); ++ ++ return retval; ++} ++ ++static const struct file_operations sst_debug_ddr_imr_dump = { ++ .open = simple_open, ++ .mmap = sst_debug_ddr_imr_dump_mmap, ++}; ++ ++static ssize_t sst_debug_ipc_write(struct file *file, ++ const char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct intel_sst_drv *ctx = (struct intel_sst_drv *)file->private_data; ++ unsigned char *buf; ++ struct sst_block *block = NULL; ++ struct ipc_dsp_hdr *dsp_hdr; ++ struct ipc_post *msg = NULL; ++ int ret, res_rqd, msg_id, drv_id; ++ u32 low_payload; ++ ++ if (count > 1024) ++ return -EINVAL; ++ ++ ret = is_fw_running(ctx); ++ if (ret) ++ return ret; ++ ++ buf = kzalloc((sizeof(unsigned char) * (count)), GFP_KERNEL); ++ if (!buf) { ++ ret = -ENOMEM; ++ goto put_pm_runtime; ++ } ++ if (copy_from_user(buf, user_buf, count)) { ++ ret = -EFAULT; ++ goto free_mem; ++ } ++ ++ if (sst_create_ipc_msg(&msg, true)) { ++ ret = -ENOMEM; ++ goto free_mem; ++ } ++ ++ msg->mrfld_header.full = *((u64 *)buf); ++ pr_debug("ipc hdr: %llx\n", msg->mrfld_header.full); ++ ++ /* Override the drv id with unique drv id */ ++ drv_id = sst_assign_pvt_id(ctx); ++ msg->mrfld_header.p.header_high.part.drv_id = drv_id; ++ ++ res_rqd = msg->mrfld_header.p.header_high.part.res_rqd; ++ msg_id = msg->mrfld_header.p.header_high.part.msg_id; ++ pr_debug("res_rqd: %d, msg_id: %d, drv_id: %d\n", ++ res_rqd, msg_id, drv_id); ++ if (res_rqd) { ++ block = sst_create_block(ctx, msg_id, drv_id); ++ if (block == NULL) { ++ ret = -ENOMEM; ++ kfree(msg); ++ goto free_mem; ++ } ++ } ++ ++ dsp_hdr = (struct ipc_dsp_hdr *)(buf + 8); ++ pr_debug("dsp hdr: %llx\n", *((u64 *)(dsp_hdr))); ++ low_payload = msg->mrfld_header.p.header_low_payload; ++ if (low_payload > (1024 - sizeof(union ipc_header_mrfld))) { ++ pr_err("Invalid low payload length: %x\n", low_payload); ++ ret = -EINVAL; ++ kfree(msg); ++ goto free_block; ++ } ++ ++ memcpy(msg->mailbox_data, (buf+(sizeof(union ipc_header_mrfld))), ++ low_payload); ++ sst_add_to_dispatch_list_and_post(ctx, msg); ++ if (res_rqd) { ++ ret = sst_wait_timeout(ctx, block); ++ if (ret) { ++ pr_err("%s: fw returned err %d\n", __func__, ret); ++ goto free_block; ++ } ++ ++ if (msg_id == IPC_GET_PARAMS) { ++ unsigned char *r = block->data; ++ memcpy(ctx->debugfs.get_params_data, r, dsp_hdr->length); ++ ctx->debugfs.get_params_len = dsp_hdr->length; ++ } ++ ++ } ++ ret = count; ++free_block: ++ if (res_rqd) ++ sst_free_block(sst_drv_ctx, block); ++free_mem: ++ kfree(buf); ++put_pm_runtime: ++ sst_pm_runtime_put(ctx); ++ return ret; ++} ++ ++static ssize_t sst_debug_ipc_read(struct file *file, ++ char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct intel_sst_drv *ctx = (struct intel_sst_drv *)file->private_data; ++ return simple_read_from_buffer(user_buf, count, ppos, ++ ctx->debugfs.get_params_data, ++ ctx->debugfs.get_params_len); ++} ++ ++static const struct file_operations sst_debug_ipc_ops = { ++ .open = simple_open, ++ .write = sst_debug_ipc_write, ++ .read = sst_debug_ipc_read, ++}; ++ ++struct sst_debug { ++ const char *name; ++ const struct file_operations *fops; ++ umode_t mode; ++}; ++ ++static const struct sst_debug sst_common_dbg_entries[] = { ++ {"runtime_pm", &sst_debug_rtpm_ops, 0600}, ++ {"shim_dump", &sst_debug_shim_ops, 0600}, ++ {"fw_clear_context", &sst_debug_fw_clear_cntx, 0600}, ++ {"fw_clear_cache", &sst_debug_fw_clear_cache, 0600}, ++ {"fw_reset_state", &sst_debug_fw_reset_state, 0600}, ++ {"fw_dwnld_mode", &sst_debug_dwnld_mode, 0600}, ++ {"iram_dump", &sst_debug_iram_dump, 0400}, ++ {"dram_dump", &sst_debug_dram_dump, 0400}, ++ {"sram_ia_lpe_mailbox", &sst_debug_sram_ia_lpe_mbox_ops, 0400}, ++ {"sram_lpe_ia_mailbox", &sst_debug_sram_lpe_ia_mbox_ops, 0400}, ++ {"README", &sst_debug_readme_ops, 0400}, ++}; ++ ++static const struct sst_debug ctp_dbg_entries[] = { ++ {"sram_lpe_debug", &sst_debug_sram_lpe_debug_ops, 0400}, ++ {"sram_lpe_checkpoint", &sst_debug_sram_lpe_checkpoint_ops, 0400}, ++ {"sram_lpe_scu_mailbox", &sst_debug_sram_lpe_scu_mbox_ops, 0400}, ++ {"sram_scu_lpe_mailbox", &sst_debug_sram_scu_lpe_mbox_ops, 0400}, ++ {"lpe_log_enable", &sst_debug_lpe_log_enable_ops, 0400}, ++ {"fw_ssp_reg", &sst_debug_ssp_reg, 0400}, ++ {"fw_dma_reg", &sst_debug_dma_reg, 0400}, ++ {"osc_clk0", &sst_debug_osc_clk0_ops, 0600}, ++}; ++ ++static const struct sst_debug mrfld_dbg_entries[] = { ++ {"sram_lpe_checkpoint", &sst_debug_sram_lpe_checkpoint_ops, 0400}, ++ {"fw_ssp_reg", &sst_debug_ssp_reg, 0400}, ++ {"fw_dma_reg", &sst_debug_dma_reg, 0400}, ++ {"ddr_imr_dump", &sst_debug_ddr_imr_dump, 0400}, ++ {"ipc", &sst_debug_ipc_ops, 0400}, ++}; ++ ++void sst_debugfs_create_files(struct intel_sst_drv *sst, ++ const struct sst_debug *entries, int size) ++{ ++ int i; ++ ++ for (i = 0; i < size; i++) { ++ struct dentry *dentry; ++ const struct sst_debug *entry = &entries[i]; ++ ++ dentry = debugfs_create_file(entry->name, entry->mode, ++ sst->debugfs.root, sst, entry->fops); ++ if (dentry == NULL) { ++ pr_err("Failed to create %s file\n", entry->name); ++ return; ++ } ++ } ++} ++ ++void sst_debugfs_init(struct intel_sst_drv *sst) ++{ ++ int size = 0; ++ const struct sst_debug *debug = NULL; ++ ++ sst->debugfs.root = debugfs_create_dir("sst", NULL); ++ if (IS_ERR(sst->debugfs.root) || !sst->debugfs.root) { ++ pr_err("Failed to create debugfs directory\n"); ++ return; ++ } ++ ++ sst_debugfs_create_files(sst, sst_common_dbg_entries, ++ ARRAY_SIZE(sst_common_dbg_entries)); ++ ++ /* Initial status is enabled */ ++ sst->debugfs.runtime_pm_status = 1; ++ ++ if (sst->pci_id == SST_MRFLD_PCI_ID) { ++ debug = mrfld_dbg_entries; ++ size = ARRAY_SIZE(mrfld_dbg_entries); ++ } else if (sst->pci_id == SST_CLV_PCI_ID) { ++ debug = ctp_dbg_entries; ++ size = ARRAY_SIZE(ctp_dbg_entries); ++ } ++ ++ if (debug) ++ sst_debugfs_create_files(sst, debug, size); ++ ++} ++ ++void sst_debugfs_exit(struct intel_sst_drv *sst) ++{ ++ if (sst->debugfs.runtime_pm_status) ++ pm_runtime_allow(sst->dev); ++ debugfs_remove_recursive(sst->debugfs.root); ++} +diff --git a/sound/soc/intel/sst/sst_drv_interface.c b/sound/soc/intel/sst/sst_drv_interface.c +new file mode 100644 +index 0000000..8250ccb +--- /dev/null ++++ b/sound/soc/intel/sst/sst_drv_interface.c +@@ -0,0 +1,1111 @@ ++/* ++ * sst_drv_interface.c - Intel SST Driver for audio engine ++ * ++ * Copyright (C) 2008-10 Intel Corp ++ * Authors: Vinod Koul ++ * Harsha Priya ++ * Dharageswari R ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "../sst_platform.h" ++#include "../platform_ipc_v2.h" ++#include "sst.h" ++ ++#define NUM_CODEC 2 ++#define MIN_FRAGMENT 2 ++#define MAX_FRAGMENT 4 ++#define MIN_FRAGMENT_SIZE (50 * 1024) ++#define MAX_FRAGMENT_SIZE (1024 * 1024) ++#define SST_GET_BYTES_PER_SAMPLE(pcm_wd_sz) (((pcm_wd_sz + 15) >> 4) << 1) ++ ++void sst_restore_fw_context(void) ++{ ++ struct snd_sst_ctxt_params fw_context; ++ struct ipc_post *msg = NULL; ++ int retval = 0; ++ struct sst_block *block; ++ ++ /* Skip the context restore, when fw_clear_context is set */ ++ /* fw_clear_context set through debugfs support */ ++ if (atomic_read(&sst_drv_ctx->fw_clear_context)) { ++ pr_debug("Skipping restore_fw_context\n"); ++ atomic_set(&sst_drv_ctx->fw_clear_context, 0); ++ return; ++ } ++ ++ pr_debug("restore_fw_context\n"); ++ /*nothing to restore*/ ++ if (!sst_drv_ctx->fw_cntx_size) ++ return; ++ pr_debug("restoring context......\n"); ++ /*send msg to fw*/ ++ retval = sst_create_block_and_ipc_msg(&msg, true, sst_drv_ctx, &block, ++ IPC_IA_SET_FW_CTXT, 0); ++ if (retval) { ++ pr_err("Can't allocate block/msg. No restore fw_context\n"); ++ return; ++ } ++ ++ sst_drv_ctx->sst_state = SST_FW_CTXT_RESTORE; ++ sst_fill_header(&msg->header, IPC_IA_SET_FW_CTXT, 1, 0); ++ ++ msg->header.part.data = sizeof(fw_context) + sizeof(u32); ++ fw_context.address = virt_to_phys((void *)sst_drv_ctx->fw_cntx); ++ fw_context.size = sst_drv_ctx->fw_cntx_size; ++ memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); ++ memcpy(msg->mailbox_data + sizeof(u32), ++ &fw_context, sizeof(fw_context)); ++ sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); ++ retval = sst_wait_timeout(sst_drv_ctx, block); ++ sst_free_block(sst_drv_ctx, block); ++ if (retval) ++ pr_err("sst_restore_fw_context..timeout!\n"); ++ return; ++} ++ ++/* ++ * sst_download_fw - download the audio firmware to DSP ++ * ++ * This function is called when the FW needs to be downloaded to SST DSP engine ++ */ ++int sst_download_fw(void) ++{ ++ int retval = 0; ++ ++ retval = sst_load_fw(); ++ if (retval) ++ return retval; ++ pr_debug("fw loaded successful!!!\n"); ++ ++ if (sst_drv_ctx->ops->restore_dsp_context) ++ sst_drv_ctx->ops->restore_dsp_context(); ++ sst_drv_ctx->sst_state = SST_FW_RUNNING; ++ return retval; ++} ++ ++int free_stream_context(unsigned int str_id) ++{ ++ struct stream_info *stream; ++ int ret = 0; ++ ++ stream = get_stream_info(str_id); ++ if (stream) { ++ /* str_id is valid, so stream is alloacted */ ++ ret = sst_free_stream(str_id); ++ if (ret) ++ sst_clean_stream(&sst_drv_ctx->streams[str_id]); ++ return ret; ++ } ++ return ret; ++} ++ ++/* ++ * sst_send_algo_param - send LPE Mixer param to SST ++ * ++ * this function sends the algo parameter to sst dsp engine ++ */ ++static int sst_send_algo_param(struct snd_ppp_params *algo_params) ++{ ++ u32 header_size = 0; ++ struct ipc_post *msg = NULL; ++ u32 ipc_msg_size = sizeof(u32) + sizeof(*algo_params) ++ - sizeof(algo_params->params) + algo_params->size; ++ u32 offset = 0; ++ ++ if (ipc_msg_size > SST_MAILBOX_SIZE) ++ return -ENOMEM; ++ if (sst_create_ipc_msg(&msg, true)) ++ return -ENOMEM; ++ sst_fill_header(&msg->header, ++ IPC_IA_ALG_PARAMS, 1, algo_params->str_id); ++ msg->header.part.data = sizeof(u32) + sizeof(*algo_params) ++ - sizeof(algo_params->params) + algo_params->size; ++ memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); ++ offset = sizeof(u32); ++ header_size = sizeof(*algo_params) - sizeof(algo_params->params); ++ memcpy(msg->mailbox_data + sizeof(u32), algo_params, ++ sizeof(*algo_params) - sizeof(algo_params->params)); ++ offset += header_size; ++ memcpy(msg->mailbox_data + offset , algo_params->params, ++ algo_params->size); ++ sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); ++ return 0; ++} ++ ++static int sst_send_lpe_mixer_algo_params(void) ++{ ++ struct snd_ppp_params algo_param; ++ struct snd_ppp_mixer_params mixer_param; ++ unsigned int input_mixer, stream_device_id; ++ int retval = 0; ++ ++ retval = intel_sst_check_device(); ++ if (retval) { ++ pr_err("sst_check_device failed %d\n", retval); ++ return retval; ++ } ++ ++ mutex_lock(&sst_drv_ctx->mixer_ctrl_lock); ++ input_mixer = (sst_drv_ctx->device_input_mixer) ++ & SST_INPUT_STREAM_MIXED; ++ pr_debug("Input Mixer settings %d", input_mixer); ++ stream_device_id = sst_drv_ctx->device_input_mixer - input_mixer; ++ algo_param.algo_id = SST_ALGO_MIXER; ++ algo_param.str_id = stream_device_id; ++ algo_param.enable = 1; ++ algo_param.operation = SST_SET_ALGO; ++ algo_param.size = sizeof(mixer_param); ++ mixer_param.type = SST_ALGO_PARAM_MIXER_STREAM_CFG; ++ mixer_param.input_stream_bitmap = input_mixer; ++ mixer_param.size = sizeof(input_mixer); ++ algo_param.params = &mixer_param; ++ mutex_unlock(&sst_drv_ctx->mixer_ctrl_lock); ++ pr_debug("setting pp param\n"); ++ pr_debug("Algo ID %d Str id %d Enable %d Size %d\n", ++ algo_param.algo_id, algo_param.str_id, ++ algo_param.enable, algo_param.size); ++ sst_send_algo_param(&algo_param); ++ sst_pm_runtime_put(sst_drv_ctx); ++ return retval; ++} ++ ++/* ++ * sst_get_stream_allocated - this function gets a stream allocated with ++ * the given params ++ * ++ * @str_param : stream params ++ * @lib_dnld : pointer to pointer of lib downlaod struct ++ * ++ * This creates new stream id for a stream, in case lib is to be downloaded to ++ * DSP, it downloads that ++ */ ++int sst_get_stream_allocated(struct snd_sst_params *str_param, ++ struct snd_sst_lib_download **lib_dnld) ++{ ++ int retval, str_id; ++ struct sst_block *block; ++ struct snd_sst_alloc_response *response; ++ struct stream_info *str_info; ++ ++ pr_debug("In %s\n", __func__); ++ block = sst_create_block(sst_drv_ctx, 0, 0); ++ if (block == NULL) ++ return -ENOMEM; ++ ++ retval = sst_drv_ctx->ops->alloc_stream((char *) str_param, block); ++ str_id = retval; ++ if (retval < 0) { ++ pr_err("sst_alloc_stream failed %d\n", retval); ++ goto free_block; ++ } ++ pr_debug("Stream allocated %d\n", retval); ++ str_info = get_stream_info(str_id); ++ if (str_info == NULL) { ++ pr_err("get stream info returned null\n"); ++ str_id = -EINVAL; ++ goto free_block; ++ } ++ ++ /* Block the call for reply */ ++ retval = sst_wait_timeout(sst_drv_ctx, block); ++ if (block->data) { ++ response = (struct snd_sst_alloc_response *)block->data; ++ retval = response->str_type.result; ++ if (!retval) ++ goto free_block; ++ ++ pr_err("sst: FW alloc failed retval %d\n", retval); ++ if (retval == SST_ERR_STREAM_IN_USE) { ++ pr_err("sst:FW not in clean state, send free for:%d\n", ++ str_id); ++ sst_free_stream(str_id); ++ *lib_dnld = NULL; ++ } ++ if (retval == SST_LIB_ERR_LIB_DNLD_REQUIRED) { ++ *lib_dnld = kzalloc(sizeof(**lib_dnld), GFP_KERNEL); ++ if (*lib_dnld == NULL) { ++ str_id = -ENOMEM; ++ goto free_block; ++ } ++ memcpy(*lib_dnld, &response->lib_dnld, sizeof(**lib_dnld)); ++ sst_clean_stream(str_info); ++ } else { ++ *lib_dnld = NULL; ++ } ++ str_id = -retval; ++ } else if (retval != 0) { ++ pr_err("sst: FW alloc failed retval %d\n", retval); ++ /* alloc failed, so reset the state to uninit */ ++ str_info->status = STREAM_UN_INIT; ++ str_id = retval; ++ } ++free_block: ++ sst_free_block(sst_drv_ctx, block); ++ return str_id; /*will ret either error (in above if) or correct str id*/ ++} ++ ++/* ++ * sst_get_sfreq - this function returns the frequency of the stream ++ * ++ * @str_param : stream params ++ */ ++int sst_get_sfreq(struct snd_sst_params *str_param) ++{ ++ switch (str_param->codec) { ++ case SST_CODEC_TYPE_PCM: ++ return str_param->sparams.uc.pcm_params.sfreq; ++ case SST_CODEC_TYPE_AAC: ++ return str_param->sparams.uc.aac_params.externalsr; ++ case SST_CODEC_TYPE_MP3: ++ return 0; ++ default: ++ return -EINVAL; ++ } ++} ++ ++/* ++ * sst_get_sfreq - this function returns the frequency of the stream ++ * ++ * @str_param : stream params ++ */ ++int sst_get_num_channel(struct snd_sst_params *str_param) ++{ ++ switch (str_param->codec) { ++ case SST_CODEC_TYPE_PCM: ++ return str_param->sparams.uc.pcm_params.num_chan; ++ case SST_CODEC_TYPE_MP3: ++ return str_param->sparams.uc.mp3_params.num_chan; ++ case SST_CODEC_TYPE_AAC: ++ return str_param->sparams.uc.aac_params.num_chan; ++ default: ++ return -EINVAL; ++ } ++} ++ ++/* ++ * sst_get_stream - this function prepares for stream allocation ++ * ++ * @str_param : stream param ++ */ ++int sst_get_stream(struct snd_sst_params *str_param) ++{ ++ int retval; ++ struct stream_info *str_info; ++ struct snd_sst_lib_download *lib_dnld; ++ ++ pr_debug("In %s\n", __func__); ++ /* stream is not allocated, we are allocating */ ++ retval = sst_get_stream_allocated(str_param, &lib_dnld); ++ ++ if (retval == -(SST_LIB_ERR_LIB_DNLD_REQUIRED)) { ++ /* codec download is required */ ++ ++ pr_debug("Codec is required.... trying that\n"); ++ if (lib_dnld == NULL) { ++ pr_err("lib download null!!! abort\n"); ++ return -EIO; ++ } ++ ++ retval = sst_load_library(lib_dnld, str_param->ops); ++ kfree(lib_dnld); ++ ++ if (!retval) { ++ pr_debug("codec was downloaded successfully\n"); ++ ++ retval = sst_get_stream_allocated(str_param, &lib_dnld); ++ if (retval <= 0) { ++ retval = -EIO; ++ goto err; ++ } ++ ++ pr_debug("Alloc done stream id %d\n", retval); ++ } else { ++ pr_debug("codec download failed\n"); ++ retval = -EIO; ++ goto err; ++ } ++ } else if (retval <= 0) { ++ retval = -EIO; ++ goto err; ++ } ++ /* store sampling freq */ ++ str_info = &sst_drv_ctx->streams[retval]; ++ str_info->sfreq = sst_get_sfreq(str_param); ++ ++err: ++ return retval; ++} ++ ++/** ++* intel_sst_check_device - checks SST device ++* ++* This utility function checks the state of SST device and downlaods FW if ++* not done, or resumes the device if suspended ++*/ ++int intel_sst_check_device(void) ++{ ++ int retval = 0; ++ ++ pr_debug("In %s\n", __func__); ++ ++ pm_runtime_get_sync(sst_drv_ctx->dev); ++ atomic_inc(&sst_drv_ctx->pm_usage_count); ++ ++ pr_debug("%s: count is %d now\n", __func__, ++ atomic_read(&sst_drv_ctx->pm_usage_count)); ++ ++ mutex_lock(&sst_drv_ctx->sst_lock); ++ if (sst_drv_ctx->sst_state == SST_UN_INIT) ++ sst_drv_ctx->sst_state = SST_START_INIT; ++ ++ if (sst_drv_ctx->sst_state == SST_START_INIT || ++ sst_drv_ctx->sst_state == SST_FW_LIB_LOAD) { ++ ++ /* FW is not downloaded */ ++ pr_debug("DSP Downloading FW now...\n"); ++ retval = sst_download_fw(); ++ if (retval) { ++ pr_err("FW download fail %x\n", retval); ++ sst_drv_ctx->sst_state = SST_UN_INIT; ++ mutex_unlock(&sst_drv_ctx->sst_lock); ++ sst_pm_runtime_put(sst_drv_ctx); ++ return retval; ++ } ++ } ++ mutex_unlock(&sst_drv_ctx->sst_lock); ++ return retval; ++} ++ ++void sst_process_mad_ops(struct work_struct *work) ++{ ++ ++ struct mad_ops_wq *mad_ops = ++ container_of(work, struct mad_ops_wq, wq); ++ int retval = 0; ++ ++ switch (mad_ops->control_op) { ++ case SST_SND_PAUSE: ++ retval = sst_pause_stream(mad_ops->stream_id); ++ break; ++ case SST_SND_RESUME: ++ retval = sst_resume_stream(mad_ops->stream_id); ++ break; ++ default: ++ pr_err(" wrong control_ops reported\n"); ++ } ++ if (retval) ++ pr_err("%s(): op: %d, retval: %d\n", ++ __func__, mad_ops->control_op, retval); ++ kfree(mad_ops); ++ return; ++} ++ ++static int sst_power_control(bool state) ++{ ++ pr_debug("%s for %d", __func__, state); ++ ++ /* should we do ref count here, or rely on pcm handle?? */ ++ if (state == true) ++ return intel_sst_check_device(); ++ else ++ return sst_pm_runtime_put(sst_drv_ctx); ++} ++/* ++ * sst_open_pcm_stream - Open PCM interface ++ * ++ * @str_param: parameters of pcm stream ++ * ++ * This function is called by MID sound card driver to open ++ * a new pcm interface ++ */ ++static int sst_open_pcm_stream(struct snd_sst_params *str_param) ++{ ++ int retval; ++ ++ if (!str_param) ++ return -EINVAL; ++ ++ pr_debug("%s: doing rtpm_get\n", __func__); ++ ++ retval = intel_sst_check_device(); ++ ++ if (retval) ++ return retval; ++ retval = sst_get_stream(str_param); ++ if (retval > 0) { ++ sst_drv_ctx->stream_cnt++; ++ } else { ++ pr_err("sst_get_stream returned err %d\n", retval); ++ sst_pm_runtime_put(sst_drv_ctx); ++ } ++ ++ return retval; ++} ++ ++static int sst_cdev_open(struct snd_sst_params *str_params, ++ struct sst_compress_cb *cb) ++{ ++ int str_id, retval; ++ struct stream_info *stream; ++ ++ pr_debug("%s: doing rtpm_get\n", __func__); ++ ++ retval = intel_sst_check_device(); ++ if (retval) ++ return retval; ++ ++ str_id = sst_get_stream(str_params); ++ if (str_id > 0) { ++ pr_debug("stream allocated in sst_cdev_open %d\n", str_id); ++ stream = &sst_drv_ctx->streams[str_id]; ++ stream->compr_cb = cb->compr_cb; ++ stream->compr_cb_param = cb->param; ++ stream->drain_notify = cb->drain_notify; ++ stream->drain_cb_param = cb->drain_cb_param; ++ } else { ++ pr_err("stream encountered error during alloc %d\n", str_id); ++ str_id = -EINVAL; ++ sst_pm_runtime_put(sst_drv_ctx); ++ } ++ return str_id; ++} ++ ++static int sst_cdev_close(unsigned int str_id) ++{ ++ int retval; ++ struct stream_info *stream; ++ ++ pr_debug("%s: Entry\n", __func__); ++ stream = get_stream_info(str_id); ++ if (!stream) { ++ pr_err("stream info is NULL for str %d!!!\n", str_id); ++ return -EINVAL; ++ } ++ ++ if (stream->status == STREAM_RESET) { ++ /* silently fail here as we have cleaned the stream */ ++ pr_debug("stream in reset state...\n"); ++ stream->status = STREAM_UN_INIT; ++ ++ retval = 0; ++ goto put; ++ } ++ ++ retval = sst_free_stream(str_id); ++put: ++ stream->compr_cb_param = NULL; ++ stream->compr_cb = NULL; ++ ++ /* The free_stream will return a error if there is no stream to free, ++ (i.e. the alloc failure case). And in this case the open does a put in ++ the error scenario, so skip in this case. ++ In the close we need to handle put in the success scenario and ++ the timeout error(EBUSY) scenario. */ ++ if (!retval || (retval == -EBUSY)) ++ sst_pm_runtime_put(sst_drv_ctx); ++ else ++ pr_err("%s: free stream returned err %d\n", __func__, retval); ++ ++ pr_debug("%s: End\n", __func__); ++ return retval; ++ ++} ++ ++static int sst_cdev_ack(unsigned int str_id, unsigned long bytes) ++{ ++ struct stream_info *stream; ++ struct snd_sst_tstamp fw_tstamp = {0,}; ++ int offset; ++ void __iomem *addr; ++ ++ pr_debug("sst: ackfor %d\n", str_id); ++ stream = get_stream_info(str_id); ++ if (!stream) ++ return -EINVAL; ++ ++ /* update bytes sent */ ++ stream->cumm_bytes += bytes; ++ pr_debug("bytes copied %d inc by %ld\n", stream->cumm_bytes, bytes); ++ ++ memcpy_fromio(&fw_tstamp, ++ ((void *)(sst_drv_ctx->mailbox + sst_drv_ctx->tstamp) ++ +(str_id * sizeof(fw_tstamp))), ++ sizeof(fw_tstamp)); ++ ++ fw_tstamp.bytes_copied = stream->cumm_bytes; ++ pr_debug("bytes sent to fw %llu inc by %ld\n", fw_tstamp.bytes_copied, ++ bytes); ++ ++ addr = ((void *)(sst_drv_ctx->mailbox + sst_drv_ctx->tstamp)) + ++ (str_id * sizeof(fw_tstamp)); ++ offset = offsetof(struct snd_sst_tstamp, bytes_copied); ++ sst_shim_write(addr, offset, fw_tstamp.bytes_copied); ++ return 0; ++ ++} ++ ++static int sst_cdev_set_metadata(unsigned int str_id, ++ struct snd_compr_metadata *metadata) ++{ ++ int retval = 0, pvt_id, len; ++ struct ipc_post *msg = NULL; ++ struct stream_info *str_info; ++ struct ipc_dsp_hdr dsp_hdr; ++ ++ pr_debug("set metadata for stream %d\n", str_id); ++ ++ str_info = get_stream_info(str_id); ++ if (!str_info) ++ return -EINVAL; ++ ++ if (sst_create_ipc_msg(&msg, 1)) ++ return -ENOMEM; ++ ++ if (!sst_drv_ctx->use_32bit_ops) { ++ pvt_id = sst_assign_pvt_id(sst_drv_ctx); ++ pr_debug("pvt id = %d\n", pvt_id); ++ pr_debug("pipe id = %d\n", str_info->pipe_id); ++ sst_fill_header_mrfld(&msg->mrfld_header, ++ IPC_CMD, str_info->task_id, 1, pvt_id); ++ ++ len = sizeof(*metadata) + sizeof(dsp_hdr); ++ msg->mrfld_header.p.header_low_payload = len; ++ sst_fill_header_dsp(&dsp_hdr, IPC_IA_SET_STREAM_PARAMS_MRFLD, ++ str_info->pipe_id, sizeof(*metadata)); ++ memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr)); ++ memcpy(msg->mailbox_data + sizeof(dsp_hdr), ++ metadata, sizeof(*metadata)); ++ } else { ++ sst_fill_header(&msg->header, IPC_IA_SET_STREAM_PARAMS, ++ 1, str_id); ++ msg->header.part.data = sizeof(u32) + sizeof(*metadata); ++ memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); ++ memcpy(msg->mailbox_data + sizeof(u32), ++ metadata, sizeof(*metadata)); ++ } ++ ++ sst_drv_ctx->ops->sync_post_message(msg); ++ return retval; ++} ++ ++static int sst_cdev_control(unsigned int cmd, unsigned int str_id) ++{ ++ pr_debug("recieved cmd %d on stream %d\n", cmd, str_id); ++ ++ if (sst_drv_ctx->sst_state == SST_UN_INIT) ++ return 0; ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ return sst_pause_stream(str_id); ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ return sst_resume_stream(str_id); ++ case SNDRV_PCM_TRIGGER_START: { ++ struct stream_info *str_info; ++ str_info = get_stream_info(str_id); ++ if (!str_info) ++ return -EINVAL; ++ str_info->prev = str_info->status; ++ str_info->status = STREAM_RUNNING; ++ return sst_start_stream(str_id); ++ } ++ case SNDRV_PCM_TRIGGER_STOP: ++ return sst_drop_stream(str_id); ++ case SND_COMPR_TRIGGER_DRAIN: ++ return sst_drain_stream(str_id, false); ++ case SND_COMPR_TRIGGER_NEXT_TRACK: ++ return sst_next_track(); ++ case SND_COMPR_TRIGGER_PARTIAL_DRAIN: ++ return sst_drain_stream(str_id, true); ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int sst_cdev_tstamp(unsigned int str_id, struct snd_compr_tstamp *tstamp) ++{ ++ struct snd_sst_tstamp fw_tstamp = {0,}; ++ struct stream_info *stream; ++ ++ memcpy_fromio(&fw_tstamp, ++ ((void *)(sst_drv_ctx->mailbox + sst_drv_ctx->tstamp) ++ +(str_id * sizeof(fw_tstamp))), ++ sizeof(fw_tstamp)); ++ ++ stream = get_stream_info(str_id); ++ if (!stream) ++ return -EINVAL; ++ pr_debug("rb_counter %llu in bytes\n", fw_tstamp.ring_buffer_counter); ++ ++ tstamp->copied_total = fw_tstamp.ring_buffer_counter; ++ tstamp->pcm_frames = fw_tstamp.frames_decoded; ++ tstamp->pcm_io_frames = div_u64(fw_tstamp.hardware_counter, ++ (u64)((stream->num_ch) * SST_GET_BYTES_PER_SAMPLE(24))); ++ tstamp->sampling_rate = fw_tstamp.sampling_frequency; ++ pr_debug("PCM = %u\n", tstamp->pcm_io_frames); ++ pr_debug("Pointer Query on strid = %d copied_total %d, decodec %d\n", ++ str_id, tstamp->copied_total, tstamp->pcm_frames); ++ pr_debug("rendered %d\n", tstamp->pcm_io_frames); ++ return 0; ++} ++ ++static int sst_cdev_caps(struct snd_compr_caps *caps) ++{ ++ caps->num_codecs = NUM_CODEC; ++ caps->min_fragment_size = MIN_FRAGMENT_SIZE; /* 50KB */ ++ caps->max_fragment_size = MAX_FRAGMENT_SIZE; /* 1024KB */ ++ caps->min_fragments = MIN_FRAGMENT; ++ caps->max_fragments = MAX_FRAGMENT; ++ caps->codecs[0] = SND_AUDIOCODEC_MP3; ++ caps->codecs[1] = SND_AUDIOCODEC_AAC; ++ return 0; ++} ++ ++static int sst_cdev_codec_caps(struct snd_compr_codec_caps *codec) ++{ ++ ++ if (codec->codec == SND_AUDIOCODEC_MP3) { ++ codec->num_descriptors = 2; ++ codec->descriptor[0].max_ch = 2; ++ codec->descriptor[0].sample_rates = SNDRV_PCM_RATE_8000_48000; ++ codec->descriptor[0].bit_rate[0] = 320; /* 320kbps */ ++ codec->descriptor[0].bit_rate[1] = 192; ++ codec->descriptor[0].num_bitrates = 2; ++ codec->descriptor[0].profiles = 0; ++ codec->descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO; ++ codec->descriptor[0].formats = 0; ++ } else if (codec->codec == SND_AUDIOCODEC_AAC) { ++ codec->num_descriptors = 2; ++ codec->descriptor[1].max_ch = 2; ++ codec->descriptor[1].sample_rates = SNDRV_PCM_RATE_8000_48000; ++ codec->descriptor[1].bit_rate[0] = 320; /* 320kbps */ ++ codec->descriptor[1].bit_rate[1] = 192; ++ codec->descriptor[1].num_bitrates = 2; ++ codec->descriptor[1].profiles = 0; ++ codec->descriptor[1].modes = 0; ++ codec->descriptor[1].formats = ++ (SND_AUDIOSTREAMFORMAT_MP4ADTS | ++ SND_AUDIOSTREAMFORMAT_RAW); ++ } else { ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++void sst_cdev_fragment_elapsed(int str_id) ++{ ++ struct stream_info *stream; ++ ++ pr_debug("fragment elapsed from firmware for str_id %d\n", str_id); ++ stream = &sst_drv_ctx->streams[str_id]; ++ if (stream->compr_cb) ++ stream->compr_cb(stream->compr_cb_param); ++} ++ ++/* ++ * sst_close_pcm_stream - Close PCM interface ++ * ++ * @str_id: stream id to be closed ++ * ++ * This function is called by MID sound card driver to close ++ * an existing pcm interface ++ */ ++static int sst_close_pcm_stream(unsigned int str_id) ++{ ++ struct stream_info *stream; ++ int retval = 0; ++ ++ pr_debug("%s: Entry\n", __func__); ++ stream = get_stream_info(str_id); ++ if (!stream) { ++ pr_err("stream info is NULL for str %d!!!\n", str_id); ++ return -EINVAL; ++ } ++ ++ if (stream->status == STREAM_RESET) { ++ /* silently fail here as we have cleaned the stream */ ++ pr_debug("stream in reset state...\n"); ++ ++ retval = 0; ++ goto put; ++ } ++ ++ retval = free_stream_context(str_id); ++put: ++ stream->pcm_substream = NULL; ++ stream->status = STREAM_UN_INIT; ++ stream->period_elapsed = NULL; ++ sst_drv_ctx->stream_cnt--; ++ ++ /* The free_stream will return a error if there is no stream to free, ++ (i.e. the alloc failure case). And in this case the open does a put in ++ the error scenario, so skip in this case. ++ In the close we need to handle put in the success scenario and ++ the timeout error(EBUSY) scenario. */ ++ if (!retval || (retval == -EBUSY)) ++ sst_pm_runtime_put(sst_drv_ctx); ++ else ++ pr_err("%s: free stream returned err %d\n", __func__, retval); ++ ++ pr_debug("%s: Exit\n", __func__); ++ return 0; ++} ++ ++int sst_send_sync_msg(int ipc, int str_id) ++{ ++ struct ipc_post *msg = NULL; ++ ++ if (sst_create_ipc_msg(&msg, false)) ++ return -ENOMEM; ++ sst_fill_header(&msg->header, ipc, 0, str_id); ++ return sst_drv_ctx->ops->sync_post_message(msg); ++} ++ ++static inline int sst_calc_tstamp(struct pcm_stream_info *info, ++ struct snd_pcm_substream *substream, ++ struct snd_sst_tstamp *fw_tstamp) ++{ ++ size_t delay_bytes, delay_frames; ++ size_t buffer_sz; ++ u32 pointer_bytes, pointer_samples; ++ ++ pr_debug("mrfld ring_buffer_counter %llu in bytes\n", ++ fw_tstamp->ring_buffer_counter); ++ pr_debug("mrfld hardware_counter %llu in bytes\n", ++ fw_tstamp->hardware_counter); ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ++ delay_bytes = (size_t) (fw_tstamp->ring_buffer_counter - ++ fw_tstamp->hardware_counter); ++ else ++ delay_bytes = (size_t) (fw_tstamp->hardware_counter - ++ fw_tstamp->ring_buffer_counter); ++ delay_frames = bytes_to_frames(substream->runtime, delay_bytes); ++ buffer_sz = snd_pcm_lib_buffer_bytes(substream); ++ div_u64_rem(fw_tstamp->ring_buffer_counter, buffer_sz, &pointer_bytes); ++ pointer_samples = bytes_to_samples(substream->runtime, pointer_bytes); ++ ++ pr_debug("pcm delay %zu in bytes\n", delay_bytes); ++ ++ info->buffer_ptr = pointer_samples / substream->runtime->channels; ++ ++ info->pcm_delay = delay_frames / substream->runtime->channels; ++ pr_debug("buffer ptr %llu pcm_delay rep: %llu\n", ++ info->buffer_ptr, info->pcm_delay); ++ return 0; ++} ++ ++static int sst_read_timestamp(struct pcm_stream_info *info) ++{ ++ struct stream_info *stream; ++ struct snd_pcm_substream *substream; ++ struct snd_sst_tstamp fw_tstamp; ++ unsigned int str_id; ++ ++ str_id = info->str_id; ++ stream = get_stream_info(str_id); ++ if (!stream) ++ return -EINVAL; ++ ++ if (!stream->pcm_substream) ++ return -EINVAL; ++ substream = stream->pcm_substream; ++ ++ memcpy_fromio(&fw_tstamp, ++ ((void *)(sst_drv_ctx->mailbox + sst_drv_ctx->tstamp) ++ + (str_id * sizeof(fw_tstamp))), ++ sizeof(fw_tstamp)); ++ return sst_calc_tstamp(info, substream, &fw_tstamp); ++} ++ ++/* ++ * sst_device_control - Set Control params ++ * ++ * @cmd: control cmd to be set ++ * @arg: command argument ++ * ++ * This function is called by MID sound card driver to set ++ * SST/Sound card controls for an opened stream. ++ * This is registered with MID driver ++ */ ++static int sst_device_control(int cmd, void *arg) ++{ ++ int retval = 0, str_id = 0; ++ ++ if (sst_drv_ctx->sst_state == SST_UN_INIT) ++ return 0; ++ ++ switch (cmd) { ++ case SST_SND_PAUSE: ++ case SST_SND_RESUME: { ++ struct mad_ops_wq *work = kzalloc(sizeof(*work), GFP_ATOMIC); ++ if (!work) ++ return -ENOMEM; ++ INIT_WORK(&work->wq, sst_process_mad_ops); ++ work->control_op = cmd; ++ work->stream_id = *(int *)arg; ++ queue_work(sst_drv_ctx->mad_wq, &work->wq); ++ break; ++ } ++ case SST_SND_START: { ++ struct stream_info *str_info; ++ int ipc; ++ str_id = *(int *)arg; ++ str_info = get_stream_info(str_id); ++ if (!str_info) ++ return -EINVAL; ++ ipc = IPC_IA_START_STREAM; ++ str_info->prev = str_info->status; ++ str_info->status = STREAM_RUNNING; ++ sst_start_stream(str_id); ++ break; ++ } ++ case SST_SND_DROP: { ++ struct stream_info *str_info; ++ int ipc; ++ str_id = *(int *)arg; ++ str_info = get_stream_info(str_id); ++ if (!str_info) ++ return -EINVAL; ++ ipc = IPC_IA_DROP_STREAM; ++ str_info->prev = STREAM_UN_INIT; ++ str_info->status = STREAM_INIT; ++ if (sst_drv_ctx->use_32bit_ops) ++ retval = sst_send_sync_msg(ipc, str_id); ++ else ++ retval = sst_drop_stream(str_id); ++ break; ++ } ++ case SST_SND_STREAM_INIT: { ++ struct pcm_stream_info *str_info; ++ struct stream_info *stream; ++ ++ pr_debug("stream init called\n"); ++ str_info = (struct pcm_stream_info *)arg; ++ str_id = str_info->str_id; ++ stream = get_stream_info(str_id); ++ if (!stream) { ++ retval = -EINVAL; ++ break; ++ } ++ pr_debug("setting the period ptrs\n"); ++ stream->pcm_substream = str_info->mad_substream; ++ stream->period_elapsed = str_info->period_elapsed; ++ stream->sfreq = str_info->sfreq; ++ stream->prev = stream->status; ++ stream->status = STREAM_INIT; ++ pr_debug("pcm_substream %p, period_elapsed %p, sfreq %d, status %d\n", ++ stream->pcm_substream, stream->period_elapsed, stream->sfreq, stream->status); ++ break; ++ } ++ ++ case SST_SND_BUFFER_POINTER: { ++ struct pcm_stream_info *stream_info; ++ ++ stream_info = (struct pcm_stream_info *)arg; ++ retval = sst_read_timestamp(stream_info); ++ pr_debug("pointer %llu, delay %llu\n", ++ stream_info->buffer_ptr, stream_info->pcm_delay); ++ break; ++ } ++ default: ++ /* Illegal case */ ++ pr_warn("illegal req\n"); ++ return -EINVAL; ++ } ++ ++ return retval; ++} ++ ++/* ++ * sst_copy_runtime_param - copy runtime params from src to dst ++ * structure. ++ * ++ *@dst: destination runtime structure ++ *@src: source runtime structure ++ * ++ * This helper function is called to copy the runtime parameter ++ * structure. ++*/ ++static int sst_copy_runtime_param(struct snd_sst_runtime_params *dst, ++ struct snd_sst_runtime_params *src) ++{ ++ dst->type = src->type; ++ dst->str_id = src->str_id; ++ dst->size = src->size; ++ if (dst->addr) { ++ pr_err("mem allocated in prev setting, use the same memory\n"); ++ return -EINVAL; ++ } ++ dst->addr = kzalloc(dst->size, GFP_KERNEL); ++ if (!dst->addr) ++ return -ENOMEM; ++ memcpy(dst->addr, src->addr, dst->size); ++ return 0; ++} ++/* ++ * sst_set_generic_params - Set generic params ++ * ++ * @cmd: control cmd to be set ++ * @arg: command argument ++ * ++ * This function is called by MID sound card driver to configure ++ * SST runtime params. ++ */ ++static int sst_set_generic_params(enum sst_controls cmd, void *arg) ++{ ++ int ret_val = 0; ++ pr_debug("Enter:%s, cmd:%d\n", __func__, cmd); ++ ++ if (NULL == arg) ++ return -EINVAL; ++ ++ switch (cmd) { ++ case SST_SET_RUNTIME_PARAMS: { ++ struct snd_sst_runtime_params *src; ++ struct snd_sst_runtime_params *dst; ++ ++ src = (struct snd_sst_runtime_params *)arg; ++ dst = &(sst_drv_ctx->runtime_param.param); ++ ret_val = sst_copy_runtime_param(dst, src); ++ break; ++ } ++ case SST_SET_ALGO_PARAMS: { ++ unsigned int device_input_mixer = *((unsigned int *)arg); ++ pr_debug("LPE mixer algo param set %x\n", device_input_mixer); ++ mutex_lock(&sst_drv_ctx->mixer_ctrl_lock); ++ sst_drv_ctx->device_input_mixer = device_input_mixer; ++ mutex_unlock(&sst_drv_ctx->mixer_ctrl_lock); ++ ret_val = sst_send_lpe_mixer_algo_params(); ++ break; ++ } ++ case SST_SET_BYTE_STREAM: { ++ ret_val = intel_sst_check_device(); ++ if (ret_val) ++ return ret_val; ++ ++ ret_val = sst_send_byte_stream_mrfld(arg); ++ sst_pm_runtime_put(sst_drv_ctx); ++ break; ++ } ++ case SST_GET_PROBE_BYTE_STREAM: { ++ struct snd_sst_probe_bytes *prb_bytes = (struct snd_sst_probe_bytes *)arg; ++ ++ if (sst_drv_ctx->probe_bytes) { ++ prb_bytes->len = sst_drv_ctx->probe_bytes->len; ++ memcpy(prb_bytes->bytes, &sst_drv_ctx->probe_bytes->bytes, prb_bytes->len); ++ } ++ break; ++ } ++ case SST_SET_PROBE_BYTE_STREAM: { ++ struct snd_sst_probe_bytes *prb_bytes = (struct snd_sst_probe_bytes *)arg; ++ ++ if (sst_drv_ctx->probe_bytes) { ++ sst_drv_ctx->probe_bytes->len = prb_bytes->len; ++ memcpy(&sst_drv_ctx->probe_bytes->bytes, prb_bytes->bytes, prb_bytes->len); ++ } ++ ++ ret_val = intel_sst_check_device(); ++ if (ret_val) ++ return ret_val; ++ ++ ret_val = sst_send_probe_bytes(sst_drv_ctx); ++ break; ++ } ++ case SST_SET_VTSV_INFO: { ++ ret_val = intel_sst_check_device(); ++ if (ret_val) ++ return ret_val; ++ ++ ret_val = sst_send_vtsv_data_to_fw(sst_drv_ctx); ++ if (ret_val) ++ pr_err("vtsv data send failed\n"); ++ sst_pm_runtime_put(sst_drv_ctx); ++ break; ++ } ++ default: ++ pr_err("Invalid cmd request:%d\n", cmd); ++ ret_val = -EINVAL; ++ } ++ return ret_val; ++} ++ ++static struct sst_ops pcm_ops = { ++ .open = sst_open_pcm_stream, ++ .device_control = sst_device_control, ++ .set_generic_params = sst_set_generic_params, ++ .close = sst_close_pcm_stream, ++ .power = sst_power_control, ++}; ++ ++static struct compress_sst_ops compr_ops = { ++ .open = sst_cdev_open, ++ .close = sst_cdev_close, ++ .control = sst_cdev_control, ++ .tstamp = sst_cdev_tstamp, ++ .ack = sst_cdev_ack, ++ .get_caps = sst_cdev_caps, ++ .get_codec_caps = sst_cdev_codec_caps, ++ .set_metadata = sst_cdev_set_metadata, ++}; ++ ++ ++static struct sst_device sst_dsp_device = { ++ .name = "Intel(R) SST LPE", ++ .dev = NULL, ++ .ops = &pcm_ops, ++ .compr_ops = &compr_ops, ++}; ++ ++/* ++ * register_sst - function to register DSP ++ * ++ * This functions registers DSP with the platform driver ++ */ ++int register_sst(struct device *dev) ++{ ++ int ret_val; ++ sst_dsp_device.dev = dev; ++ ret_val = sst_register_dsp(&sst_dsp_device); ++ if (ret_val) ++ pr_err("Unable to register DSP with platform driver\n"); ++ ++ return ret_val; ++} ++ ++int unregister_sst(struct device *dev) ++{ ++ return sst_unregister_dsp(&sst_dsp_device); ++} +diff --git a/sound/soc/intel/sst/sst_dsp.c b/sound/soc/intel/sst/sst_dsp.c +new file mode 100644 +index 0000000..edfe471 +--- /dev/null ++++ b/sound/soc/intel/sst/sst_dsp.c +@@ -0,0 +1,1995 @@ ++/* ++ * sst_dsp.c - Intel SST Driver for audio engine ++ * ++ * Copyright (C) 2008-10 Intel Corp ++ * Authors: Vinod Koul ++ * Harsha Priya ++ * Dharageswari R ++ * KP Jeeja ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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. ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * This file contains all dsp controlling functions like firmware download, ++ * setting/resetting dsp cores, etc ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "../sst_platform.h" ++#include "../platform_ipc_v2.h" ++#include "sst.h" ++#include "sst_trace.h" ++ ++#ifndef CONFIG_X86_64 ++#define MEMCPY_TOIO memcpy_toio ++#else ++#define MEMCPY_TOIO memcpy32_toio ++#endif ++ ++static struct sst_module_info sst_modules_mrfld[] = { ++ {"mp3_dec", SST_CODEC_TYPE_MP3, 0, SST_LIB_NOT_FOUND}, ++ {"aac_dec", SST_CODEC_TYPE_AAC, 0, SST_LIB_NOT_FOUND}, ++ {"audclass_lib", SST_ALGO_AUDCLASSIFIER, 0, SST_LIB_NOT_FOUND}, ++ {"vtsv_lib", SST_ALGO_VTSV, 0, SST_LIB_NOT_FOUND}, ++ {"geq_lib", SST_ALGO_GEQ, 0, SST_LIB_NOT_FOUND}, ++}; ++ ++static struct sst_module_info sst_modules_byt[] = { ++ {"mp3_dec", SST_CODEC_TYPE_MP3, 0, SST_LIB_NOT_FOUND}, ++ {"aac_dec", SST_CODEC_TYPE_AAC, 0, SST_LIB_NOT_FOUND}, ++}; ++ ++/** ++ * memcpy32_toio: Copy using writel commands ++ * ++ * This is needed because the hardware does not support ++ * 64-bit moveq insructions while writing to PCI MMIO ++ */ ++void memcpy32_toio(void *dst, const void *src, int count) ++{ ++ int i; ++ const u32 *src_32 = src; ++ u32 *dst_32 = dst; ++ ++ for (i = 0; i < count/sizeof(u32); i++) ++ writel(*src_32++, dst_32++); ++} ++ ++/** ++ * intel_sst_reset_dsp_medfield - Resetting SST DSP ++ * ++ * This resets DSP in case of Medfield platfroms ++ */ ++int intel_sst_reset_dsp_mfld(void) ++{ ++ union config_status_reg csr; ++ ++ pr_debug("Resetting the DSP in medfield\n"); ++ mutex_lock(&sst_drv_ctx->csr_lock); ++ csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); ++ csr.full |= 0x382; ++ csr.part.run_stall = 0x1; ++ sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); ++ mutex_unlock(&sst_drv_ctx->csr_lock); ++ ++ return 0; ++} ++ ++/** ++ * sst_start_medfield - Start the SST DSP processor ++ * ++ * This starts the DSP in MRST platfroms ++ */ ++int sst_start_mfld(void) ++{ ++ union config_status_reg csr; ++ ++ mutex_lock(&sst_drv_ctx->csr_lock); ++ csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); ++ csr.part.bypass = 0; ++ sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); ++ csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); ++ csr.part.mfld_strb = 1; ++ sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); ++ csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); ++ csr.part.run_stall = 0; ++ csr.part.sst_reset = 0; ++ pr_debug("Starting the DSP_medfld %x\n", csr.full); ++ sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); ++ pr_debug("Starting the DSP_medfld\n"); ++ mutex_unlock(&sst_drv_ctx->csr_lock); ++ ++ return 0; ++} ++/** ++ * intel_sst_reset_dsp_mrfld - Resetting SST DSP ++ * ++ * This resets DSP in case of MRFLD platfroms ++ */ ++int intel_sst_reset_dsp_mrfld(void) ++{ ++ union config_status_reg_mrfld csr; ++ ++ pr_debug("sst: Resetting the DSP in mrfld\n"); ++ mutex_lock(&sst_drv_ctx->csr_lock); ++ csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR); ++ ++ pr_debug("value:0x%llx\n", csr.full); ++ ++ csr.full |= 0x7; ++ sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full); ++ csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR); ++ ++ pr_debug("value:0x%llx\n", csr.full); ++ ++ csr.full &= ~(0x1); ++ sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full); ++ ++ csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR); ++ pr_debug("value:0x%llx\n", csr.full); ++ mutex_unlock(&sst_drv_ctx->csr_lock); ++ return 0; ++} ++ ++/** ++ * sst_start_merrifield - Start the SST DSP processor ++ * ++ * This starts the DSP in MERRIFIELD platfroms ++ */ ++int sst_start_mrfld(void) ++{ ++ union config_status_reg_mrfld csr; ++ ++ pr_debug("sst: Starting the DSP in mrfld LALALALA\n"); ++ mutex_lock(&sst_drv_ctx->csr_lock); ++ csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR); ++ pr_debug("value:0x%llx\n", csr.full); ++ ++ csr.full |= 0x7; ++ sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full); ++ ++ csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR); ++ pr_debug("value:0x%llx\n", csr.full); ++ ++ csr.part.xt_snoop = 1; ++ csr.full &= ~(0x5); ++ sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full); ++ ++ csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR); ++ pr_debug("sst: Starting the DSP_merrifield:%llx\n", csr.full); ++ mutex_unlock(&sst_drv_ctx->csr_lock); ++ return 0; ++} ++ ++/** ++ * intel_sst_set_bypass - Sets/clears the bypass bits ++ * ++ * This sets/clears the bypass bits ++ */ ++void intel_sst_set_bypass_mfld(bool set) ++{ ++ union config_status_reg csr; ++ ++ mutex_lock(&sst_drv_ctx->csr_lock); ++ csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); ++ if (set == true) ++ csr.full |= 0x380; ++ else ++ csr.part.bypass = 0; ++ pr_debug("SetupByPass set %d Val 0x%x\n", set, csr.full); ++ sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); ++ mutex_unlock(&sst_drv_ctx->csr_lock); ++ ++} ++#define SST_CALC_DMA_DSTN(lpe_viewpt_rqd, ia_viewpt_addr, elf_paddr, \ ++ lpe_viewpt_addr) ((lpe_viewpt_rqd) ? \ ++ elf_paddr : (ia_viewpt_addr + elf_paddr - lpe_viewpt_addr)) ++ ++static int sst_fill_dstn(struct intel_sst_drv *sst, struct sst_info info, ++ Elf32_Phdr *pr, void **dstn, unsigned int *dstn_phys, int *mem_type) ++{ ++#ifdef MRFLD_WORD_WA ++ /* work arnd-since only 4 byte align copying is only allowed for ICCM */ ++ if ((pr->p_paddr >= info.iram_start) && (pr->p_paddr < info.iram_end)) { ++ size_t data_size = pr->p_filesz % SST_ICCM_BOUNDARY; ++ ++ if (data_size) ++ pr->p_filesz += 4 - data_size; ++ *dstn = sst->iram + (pr->p_paddr - info.iram_start); ++ *dstn_phys = SST_CALC_DMA_DSTN(info.lpe_viewpt_rqd, ++ sst->iram_base, pr->p_paddr, info.iram_start); ++ *mem_type = 1; ++ } ++#else ++ if ((pr->p_paddr >= info.iram_start) && ++ (pr->p_paddr < info.iram_end)) { ++ ++ *dstn = sst->iram + (pr->p_paddr - info.iram_start); ++ *dstn_phys = SST_CALC_DMA_DSTN(info.lpe_viewpt_rqd, ++ sst->iram_base, pr->p_paddr, info.iram_start); ++ *mem_type = 1; ++ } ++#endif ++ else if ((pr->p_paddr >= info.dram_start) && ++ (pr->p_paddr < info.dram_end)) { ++ ++ *dstn = sst->dram + (pr->p_paddr - info.dram_start); ++ *dstn_phys = SST_CALC_DMA_DSTN(info.lpe_viewpt_rqd, ++ sst->dram_base, pr->p_paddr, info.dram_start); ++ *mem_type = 1; ++ } else if ((pr->p_paddr >= info.imr_start) && ++ (pr->p_paddr < info.imr_end)) { ++ ++ *dstn = sst->ddr + (pr->p_paddr - info.imr_start); ++ *dstn_phys = sst->ddr_base + pr->p_paddr - info.imr_start; ++ *mem_type = 0; ++ } else { ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static void sst_fill_info(struct intel_sst_drv *sst, ++ struct sst_info *info) ++{ ++ /* first we setup addresses to be used for elf sections */ ++ if (sst->info.iram_use) { ++ info->iram_start = sst->info.iram_start; ++ info->iram_end = sst->info.iram_end; ++ } else { ++ info->iram_start = sst->iram_base; ++ info->iram_end = sst->iram_end; ++ } ++ if (sst->info.dram_use) { ++ info->dram_start = sst->info.dram_start; ++ info->dram_end = sst->info.dram_end; ++ } else { ++ info->dram_start = sst->dram_base; ++ info->dram_end = sst->dram_end; ++ } ++ if (sst->info.imr_use) { ++ info->imr_start = sst->info.imr_start; ++ info->imr_end = sst->info.imr_end; ++ } else { ++ info->imr_start = relocate_imr_addr_mrfld(sst->ddr_base); ++ info->imr_end = relocate_imr_addr_mrfld(sst->ddr_end); ++ } ++ ++ info->lpe_viewpt_rqd = sst->info.lpe_viewpt_rqd; ++ info->dma_max_len = sst->info.dma_max_len; ++ pr_debug("%s: dma_max_len 0x%x", __func__, info->dma_max_len); ++} ++ ++static inline int sst_validate_elf(const struct firmware *sst_bin, bool dynamic) ++{ ++ Elf32_Ehdr *elf; ++ ++ BUG_ON(!sst_bin); ++ ++ pr_debug("IN %s\n", __func__); ++ ++ elf = (Elf32_Ehdr *)sst_bin->data; ++ ++ if ((elf->e_ident[0] != 0x7F) || (elf->e_ident[1] != 'E') || ++ (elf->e_ident[2] != 'L') || (elf->e_ident[3] != 'F')) { ++ pr_debug("ELF Header Not found!%zu\n", sst_bin->size); ++ return -EINVAL; ++ } ++ ++ if (dynamic == true) { ++ if (elf->e_type != ET_DYN) { ++ pr_err("Not a dynamic loadable library\n"); ++ return -EINVAL; ++ } ++ } ++ pr_debug("Valid ELF Header...%zu\n", sst_bin->size); ++ return 0; ++} ++ ++/** ++ * sst_validate_fw_image - validates the firmware signature ++ * ++ * @sst_fw_in_mem : pointer to audio FW ++ * @size : size of the firmware ++ * @module : points to the FW modules ++ * @num_modules : points to the num of modules ++ * This function validates the header signature in the FW image ++ */ ++static int sst_validate_fw_image(const void *sst_fw_in_mem, unsigned long size, ++ struct fw_module_header **module, u32 *num_modules) ++{ ++ struct fw_header *header; ++ ++ pr_debug("%s\n", __func__); ++ ++ /* Read the header information from the data pointer */ ++ header = (struct fw_header *)sst_fw_in_mem; ++ pr_debug("header sign=%s size=%x modules=%x fmt=%x size=%zx\n", ++ header->signature, header->file_size, header->modules, ++ header->file_format, sizeof(*header)); ++ ++ /* verify FW */ ++ if ((strncmp(header->signature, SST_FW_SIGN, 4) != 0) || ++ (size != header->file_size + sizeof(*header))) { ++ /* Invalid FW signature */ ++ pr_err("InvalidFW sign/filesize mismatch\n"); ++ return -EINVAL; ++ } ++ *num_modules = header->modules; ++ *module = (void *)sst_fw_in_mem + sizeof(*header); ++ ++ return 0; ++} ++ ++/** ++ * sst_validate_library - validates the library signature ++ * ++ * @fw_lib : pointer to FW library ++ * @slot : pointer to the lib slot info ++ * @entry_point : out param, which contains the module entry point ++ * This function is called before downloading the codec/postprocessing ++ * library ++ */ ++static int sst_validate_library(const struct firmware *fw_lib, ++ struct lib_slot_info *slot, ++ u32 *entry_point) ++{ ++ struct fw_header *header; ++ struct fw_module_header *module; ++ struct fw_block_info *block; ++ unsigned int n_blk, isize = 0, dsize = 0; ++ int err = 0; ++ ++ header = (struct fw_header *)fw_lib->data; ++ if (header->modules != 1) { ++ pr_err("Module no mismatch found\n"); ++ err = -EINVAL; ++ goto exit; ++ } ++ module = (void *)fw_lib->data + sizeof(*header); ++ *entry_point = module->entry_point; ++ pr_debug("Module entry point 0x%x\n", *entry_point); ++ pr_debug("Module Sign %s, Size 0x%x, Blocks 0x%x Type 0x%x\n", ++ module->signature, module->mod_size, ++ module->blocks, module->type); ++ ++ block = (void *)module + sizeof(*module); ++ for (n_blk = 0; n_blk < module->blocks; n_blk++) { ++ switch (block->type) { ++ case SST_IRAM: ++ isize += block->size; ++ break; ++ case SST_DRAM: ++ dsize += block->size; ++ break; ++ default: ++ pr_err("Invalid block type for 0x%x\n", n_blk); ++ err = -EINVAL; ++ goto exit; ++ } ++ block = (void *)block + sizeof(*block) + block->size; ++ } ++ if (isize > slot->iram_size || dsize > slot->dram_size) { ++ pr_err("library exceeds size allocated\n"); ++ err = -EINVAL; ++ goto exit; ++ } else ++ pr_debug("Library is safe for download...\n"); ++ ++ pr_debug("iram 0x%x, dram 0x%x, iram 0x%x, dram 0x%x\n", ++ isize, dsize, slot->iram_size, slot->dram_size); ++exit: ++ return err; ++ ++} ++ ++static bool chan_filter(struct dma_chan *chan, void *param) ++{ ++ struct sst_dma *dma = (struct sst_dma *)param; ++ ++ /* we only need MID_DMAC1 as that can access DSP RAMs*/ ++ if (chan->device->dev == dma->dev) ++ return true; ++ ++ return false; ++} ++ ++static unsigned int ++sst_get_elf_sg_len(struct intel_sst_drv *sst, Elf32_Ehdr *elf, Elf32_Phdr *pr, ++ struct sst_info info) ++{ ++ unsigned int i = 0, count = 0; ++ ++ pr_debug("in %s: dma_max_len 0x%x\n", __func__, info.dma_max_len); ++ ++ while (i < elf->e_phnum) { ++ if (pr[i].p_type == PT_LOAD) { ++ ++ if ((pr[i].p_paddr >= info.iram_start) && ++ (pr[i].p_paddr < info.iram_end && ++ pr[i].p_filesz)) { ++ count += (pr[i].p_filesz) / info.dma_max_len; ++ ++ if ((pr[i].p_filesz) % info.dma_max_len) ++ count++; ++ ++ } else if ((pr[i].p_paddr >= info.dram_start) && ++ (pr[i].p_paddr < info.dram_end && ++ pr[i].p_filesz)) { ++ count += (pr[i].p_filesz) / info.dma_max_len; ++ ++ if ((pr[i].p_filesz) % info.dma_max_len) ++ count++; ++ ++ } else if ((pr[i].p_paddr >= info.imr_start) && ++ (pr[i].p_paddr < info.imr_end && ++ pr[i].p_filesz)) { ++ count += (pr[i].p_filesz) / info.dma_max_len; ++ ++ if ((pr[i].p_filesz) % info.dma_max_len) ++ count++; ++ } ++ } ++ i++; ++ } ++ ++ pr_debug("gotcha count %d\n", count); ++ return count; ++} ++ ++static int ++sst_init_dma_sg_list(struct intel_sst_drv *sst, unsigned int len, ++ struct scatterlist **src, struct scatterlist **dstn) ++{ ++ struct scatterlist *sg_src = NULL, *sg_dst = NULL; ++ ++ sg_src = kzalloc(sizeof(*sg_src)*(len), GFP_KERNEL); ++ if (NULL == sg_src) ++ return -ENOMEM; ++ sg_init_table(sg_src, len); ++ sg_dst = kzalloc(sizeof(*sg_dst)*(len), GFP_KERNEL); ++ if (NULL == sg_dst) { ++ kfree(sg_src); ++ return -ENOMEM; ++ } ++ sg_init_table(sg_dst, len); ++ *src = sg_src; ++ *dstn = sg_dst; ++ ++ return 0; ++} ++ ++static int sst_alloc_dma_chan(struct sst_dma *dma) ++{ ++ dma_cap_mask_t mask; ++ struct intel_mid_dma_slave *slave = &dma->slave; ++ int retval; ++ struct pci_dev *dmac = NULL; ++ const char *hid; ++ ++ pr_debug("%s\n", __func__); ++ dma->dev = NULL; ++ dma_cap_zero(mask); ++ dma_cap_set(DMA_MEMCPY, mask); ++ ++ if (sst_drv_ctx->pci_id == SST_CLV_PCI_ID) ++ dmac = pci_get_device(PCI_VENDOR_ID_INTEL, ++ PCI_DMAC_CLV_ID, NULL); ++ else if (sst_drv_ctx->pci_id == SST_MRFLD_PCI_ID) ++ dmac = pci_get_device(PCI_VENDOR_ID_INTEL, ++ PCI_DMAC_MRFLD_ID, NULL); ++ else if (sst_drv_ctx->pci_id == SST_BYT_PCI_ID) { ++ hid = sst_drv_ctx->hid; ++ if (!strncmp(hid, "LPE0F281", 8)) ++ dma->dev = intel_mid_get_acpi_dma("DMA0F28"); ++ else if (!strncmp(hid, "80860F28", 8)) ++ dma->dev = intel_mid_get_acpi_dma("ADMA0F28"); ++ else if (!strncmp(hid, "808622A8", 8)) ++ dma->dev = intel_mid_get_acpi_dma("ADMA22A8"); ++ else if (!strncmp(hid, "LPE0F28", 7)) ++ dma->dev = intel_mid_get_acpi_dma("DMA0F28"); ++ } ++ ++ if (!dmac && !dma->dev) { ++ pr_err("Can't find DMAC\n"); ++ return -ENODEV; ++ } ++ if (dmac) ++ dma->dev = &dmac->dev; ++ ++ dma->ch = dma_request_channel(mask, chan_filter, dma); ++ if (!dma->ch) { ++ pr_err("unable to request dma channel\n"); ++ return -EIO; ++ } ++ ++ slave->dma_slave.direction = DMA_MEM_TO_MEM; ++ slave->hs_mode = 0; ++ slave->cfg_mode = LNW_DMA_MEM_TO_MEM; ++ slave->dma_slave.src_addr_width = slave->dma_slave.dst_addr_width = ++ DMA_SLAVE_BUSWIDTH_4_BYTES; ++ slave->dma_slave.src_maxburst = slave->dma_slave.dst_maxburst = ++ LNW_DMA_MSIZE_16; ++ ++ retval = dmaengine_slave_config(dma->ch, &slave->dma_slave); ++ if (retval) { ++ pr_err("unable to set slave config, err %d\n", retval); ++ dma_release_channel(dma->ch); ++ return -EIO; ++ } ++ return retval; ++} ++ ++static void sst_dma_transfer_complete(void *arg) ++{ ++ sst_drv_ctx = (struct intel_sst_drv *)arg; ++ pr_debug(" sst_dma_transfer_complete\n"); ++ sst_wake_up_block(sst_drv_ctx, 0, FW_DWNL_ID, FW_DWNL_ID, NULL, 0); ++} ++ ++static inline int sst_dma_wait_for_completion(struct intel_sst_drv *sst) ++{ ++ int ret = 0; ++ struct sst_block *block; ++ /* call prep and wait */ ++ sst->desc->callback = sst_dma_transfer_complete; ++ sst->desc->callback_param = sst; ++ ++ block = sst_create_block(sst, FW_DWNL_ID, FW_DWNL_ID); ++ if (block == NULL) ++ return -ENOMEM; ++ ++ sst->desc->tx_submit(sst_drv_ctx->desc); ++ ret = sst_wait_timeout(sst, block); ++ if (ret) ++ dma_wait_for_async_tx(sst_drv_ctx->desc); ++ sst_free_block(sst, block); ++ return ret; ++} ++ ++static int sst_dma_firmware(struct sst_dma *dma, struct sst_sg_list *sg_list) ++{ ++ int retval = 0; ++ enum dma_ctrl_flags flag = DMA_CTRL_ACK; ++ struct scatterlist *sg_src_list, *sg_dst_list; ++ int length; ++ pr_debug("%s: use_lli %d\n", __func__, sst_drv_ctx->use_lli); ++ ++ sg_src_list = sg_list->src; ++ sg_dst_list = sg_list->dst; ++ length = sg_list->list_len; ++ ++ /* BY default PIMR is unsmasked ++ * FW gets unmaksed dma intr too, so mask it for FW to execute on mrfld ++ */ ++ if (sst_drv_ctx->pci_id == SST_MRFLD_PCI_ID || ++ sst_drv_ctx->pci_id == SST_BYT_PCI_ID) ++ sst_shim_write(sst_drv_ctx->shim, SST_PIMR, 0xFFFF0034); ++ ++ if (sst_drv_ctx->use_lli) { ++ sst_drv_ctx->desc = dma->ch->device->device_prep_dma_sg(dma->ch, ++ sg_dst_list, length, ++ sg_src_list, length, flag); ++ if (!sst_drv_ctx->desc) ++ return -EFAULT; ++ retval = sst_dma_wait_for_completion(sst_drv_ctx); ++ if (retval) ++ pr_err("sst_dma_firmware..timeout!\n"); ++ } else { ++ struct scatterlist *sg; ++ dma_addr_t src_addr, dstn_addr; ++ int i = 0; ++ ++ /* dma single block mode */ ++ for_each_sg(sg_src_list, sg, length, i) { ++ pr_debug("dma desc %d, length %d\n", i, sg->length); ++ src_addr = sg_phys(sg); ++ dstn_addr = sg_phys(sg_dst_list); ++ if (sg_dst_list) ++ sg_dst_list = sg_next(sg_dst_list); ++ sst_drv_ctx->desc = dma->ch->device->device_prep_dma_memcpy( ++ dma->ch, dstn_addr, src_addr, sg->length, flag); ++ if (!sst_drv_ctx->desc) ++ return -EFAULT; ++ retval = sst_dma_wait_for_completion(sst_drv_ctx); ++ if (retval) ++ pr_err("sst_dma_firmware..timeout!\n"); ++ ++ } ++ } ++ ++ return retval; ++} ++ ++/* ++ * sst_fill_sglist - Fill the sg list ++ * ++ * @from: src address of the fw ++ * @to: virtual address of IRAM/DRAM ++ * @block_size: size of the block ++ * @sg_src: source scatterlist pointer ++ * @sg_dst: Destination scatterlist pointer ++ * @fw_sg_list: Pointer to the sg_list ++ * @dma_max_len: maximum len of the DMA block ++ * ++ * Parses modules that need to be placed in SST IRAM and DRAM ++ * and stores them in a sg list for transfer ++ * returns error or 0 if list creation fails or pass. ++ */ ++static int sst_fill_sglist(unsigned long from, unsigned long to, ++ u32 block_size, struct scatterlist **sg_src, struct scatterlist **sg_dstn, ++ struct sst_sg_list *fw_sg_list, u32 dma_max_len) ++{ ++ u32 offset = 0; ++ int len = 0; ++ unsigned long dstn, src; ++ ++ pr_debug("%s entry", __func__); ++ if (!sg_src || !sg_dstn) ++ return -EINVAL; ++ ++ do { ++ dstn = (unsigned long) (to + offset); ++ src = (unsigned long) (from + offset); ++ ++ /* split blocks to dma_max_len */ ++ ++ len = block_size - offset; ++ pr_debug("DMA blk src %lx,dstn %lx,len %d,offset %d, size %d\n", ++ src, dstn, len, offset, block_size); ++ if (len > dma_max_len) { ++ pr_debug("block size exceeds %d\n", dma_max_len); ++ len = dma_max_len; ++ offset += len; ++ } else { ++ pr_debug("Node length less that %d\n", dma_max_len); ++ offset = 0; ++ } ++ ++ if (!(*sg_src) || !(*sg_dstn)) ++ return -ENOMEM; ++ ++ sg_set_page(*sg_src, virt_to_page((void *) src), len, ++ offset_in_page((void *) src)); ++ sg_set_page(*sg_dstn, virt_to_page((void *) dstn), len, ++ offset_in_page((void *) dstn)); ++ ++ *sg_src = sg_next(*sg_src); ++ *sg_dstn = sg_next(*sg_dstn); ++ ++ /* TODO: is sg_idx required? */ ++ if (sst_drv_ctx->info.use_elf == true) ++ fw_sg_list->sg_idx++; ++ } while (offset > 0); ++ ++ return 0; ++} ++ ++static int sst_parse_elf_module_dma(struct intel_sst_drv *sst, const void *fw, ++ struct sst_info info, Elf32_Phdr *pr, ++ struct scatterlist **sg_src, struct scatterlist **sg_dstn, ++ struct sst_sg_list *fw_sg_list) ++{ ++ unsigned long dstn, src; ++ unsigned int dstn_phys; ++ int ret_val = 0; ++ int mem_type; ++ ++ ret_val = sst_fill_dstn(sst, info, pr, (void *)&dstn, &dstn_phys, &mem_type); ++ if (ret_val) ++ return ret_val; ++ ++ dstn = (unsigned long) phys_to_virt(dstn_phys); ++ src = (unsigned long) (fw + pr->p_offset); ++ ++ ret_val = sst_fill_sglist(src, dstn, pr->p_filesz, ++ sg_src, sg_dstn, fw_sg_list, sst->info.dma_max_len); ++ ++ return ret_val; ++} ++ ++static int ++sst_parse_elf_fw_dma(struct intel_sst_drv *sst, const void *fw_in_mem, ++ struct sst_sg_list *fw_sg_list) ++{ ++ int i = 0, ret = 0; ++ Elf32_Ehdr *elf; ++ Elf32_Phdr *pr; ++ struct sst_info info; ++ struct scatterlist *sg_src = NULL, *sg_dst = NULL; ++ unsigned int sg_len; ++ ++ BUG_ON(!fw_in_mem); ++ ++ elf = (Elf32_Ehdr *)fw_in_mem; ++ pr = (Elf32_Phdr *) (fw_in_mem + elf->e_phoff); ++ pr_debug("%s entry\n", __func__); ++ ++ sst_fill_info(sst, &info); ++ ++ sg_len = sst_get_elf_sg_len(sst, elf, pr, info); ++ if (sg_len == 0) { ++ pr_err("we got NULL sz ELF, abort\n"); ++ return -EIO; ++ } ++ ++ if (sst_init_dma_sg_list(sst, sg_len, &sg_src, &sg_dst)) { ++ ret = -ENOMEM; ++ goto err1; ++ } ++ ++ fw_sg_list->src = sg_src; ++ fw_sg_list->dst = sg_dst; ++ fw_sg_list->list_len = sg_len; ++ fw_sg_list->sg_idx = 0; ++ ++ while (i < elf->e_phnum) { ++ if ((pr[i].p_type == PT_LOAD) && (pr[i].p_filesz)) { ++ ret = sst_parse_elf_module_dma(sst, fw_in_mem, info, ++ &pr[i], &sg_src, &sg_dst, fw_sg_list); ++ if (ret) ++ goto err; ++ } ++ i++; ++ } ++ return 0; ++err: ++ kfree(fw_sg_list->src); ++ kfree(fw_sg_list->dst); ++err1: ++ fw_sg_list->src = NULL; ++ fw_sg_list->dst = NULL; ++ fw_sg_list->list_len = 0; ++ fw_sg_list->sg_idx = 0; ++ ++ return ret; ++} ++ ++/** ++ * sst_parse_module_dma - Parse audio FW modules and populate the dma list ++ * ++ * @sst_ctx : sst driver context ++ * @module : FW module header ++ * @sg_list : Pointer to the sg_list to be populated ++ * Count the length for scattergather list ++ * and create the scattergather list of same length ++ * returns error or 0 if module sizes are proper ++ */ ++static int sst_parse_module_dma(struct intel_sst_drv *sst_ctx, ++ struct fw_module_header *module, ++ struct sst_sg_list *sg_list) ++{ ++ struct fw_block_info *block; ++ u32 count; ++ unsigned long ram, src; ++ int retval, sg_len = 0; ++ struct scatterlist *sg_src, *sg_dst; ++ ++ pr_debug("module sign %s size %x blocks %x type %x\n", ++ module->signature, module->mod_size, ++ module->blocks, module->type); ++ pr_debug("module entrypoint 0x%x\n", module->entry_point); ++ ++ block = (void *)module + sizeof(*module); ++ ++ for (count = 0; count < module->blocks; count++) { ++ sg_len += (block->size) / sst_drv_ctx->info.dma_max_len; ++ ++ if (block->size % sst_drv_ctx->info.dma_max_len) ++ sg_len = sg_len + 1; ++ block = (void *)block + sizeof(*block) + block->size; ++ } ++ ++ if (sst_init_dma_sg_list(sst_ctx, sg_len, &sg_src, &sg_dst)) { ++ retval = -ENOMEM; ++ goto err1; ++ } ++ ++ sg_list->src = sg_src; ++ sg_list->dst = sg_dst; ++ sg_list->list_len = sg_len; ++ ++ block = (void *)module + sizeof(*module); ++ ++ for (count = 0; count < module->blocks; count++) { ++ if (block->size <= 0) { ++ pr_err("block size invalid\n"); ++ retval = -EINVAL; ++ goto err; ++ } ++ switch (block->type) { ++ case SST_IRAM: ++ ram = sst_ctx->iram_base; ++ break; ++ case SST_DRAM: ++ ram = sst_ctx->dram_base; ++ break; ++ default: ++ pr_err("wrong ram type0x%x in block0x%x\n", ++ block->type, count); ++ retval = -EINVAL; ++ goto err; ++ } ++ ++ /*converting from physical to virtual because ++ scattergather list works on virtual pointers*/ ++ ram = (unsigned long) phys_to_virt(ram); ++ ram = (unsigned long)(ram + block->ram_offset); ++ src = (unsigned long) (void *)block + sizeof(*block); ++ ++ retval = sst_fill_sglist(src, ram, ++ block->size, &sg_src, &sg_dst, ++ sg_list, sst_ctx->info.dma_max_len); ++ if (retval) ++ goto err; ++ ++ block = (void *)block + sizeof(*block) + block->size; ++ } ++ return 0; ++err: ++ kfree(sg_list->src); ++ kfree(sg_list->dst); ++err1: ++ sg_list->src = NULL; ++ sg_list->dst = NULL; ++ sg_list->list_len = 0; ++ ++ return retval; ++} ++ ++/** ++ * sst_parse_fw_dma - parse the firmware image & populate the list for dma ++ * ++ * @sst_fw_in_mem : pointer to audio fw ++ * @size : size of the firmware ++ * @fw_list : pointer to sst_sg_list to be populated ++ * This function parses the FW image and saves the parsed image in the list ++ * for dma ++ */ ++static int sst_parse_fw_dma(const void *sst_fw_in_mem, unsigned long size, ++ struct sst_sg_list *fw_list) ++{ ++ struct fw_module_header *module; ++ u32 count, num_modules; ++ int ret_val; ++ ++ ret_val = sst_validate_fw_image(sst_fw_in_mem, size, ++ &module, &num_modules); ++ if (ret_val) ++ return ret_val; ++ ++ for (count = 0; count < num_modules; count++) { ++ /* module */ ++ ret_val = sst_parse_module_dma(sst_drv_ctx, module, fw_list); ++ if (ret_val) ++ return ret_val; ++ module = (void *)module + sizeof(*module) + module->mod_size ; ++ } ++ ++ return 0; ++} ++ ++static void sst_dma_free_resources(struct sst_dma *dma) ++{ ++ pr_debug("entry:%s\n", __func__); ++ ++ dma_release_channel(dma->ch); ++} ++ ++void sst_fill_config(struct intel_sst_drv *sst_ctx, unsigned int offset) ++{ ++ struct sst_fill_config sst_config; ++ ++ if (!(sst_ctx->pdata->bdata && sst_ctx->pdata->pdata)) ++ return; ++ ++ sst_config.sign = SST_CONFIG_SSP_SIGN; ++ memcpy(&sst_config.sst_bdata, sst_ctx->pdata->bdata, sizeof(struct sst_board_config_data)); ++ memcpy(&sst_config.sst_pdata, sst_ctx->pdata->pdata, sizeof(struct sst_platform_config_data)); ++ sst_config.shim_phy_add = sst_ctx->shim_phy_add; ++ sst_config.mailbox_add = sst_ctx->mailbox_add; ++ MEMCPY_TOIO(sst_ctx->dram + offset, &sst_config, sizeof(sst_config)); ++ ++} ++ ++/** ++ * sst_do_dma - function allocs and initiates the DMA ++ * ++ * @sg_list: Pointer to dma list on which the dma needs to be initiated ++ * ++ * Triggers the DMA ++ */ ++static int sst_do_dma(struct sst_sg_list *sg_list) ++{ ++ int ret_val; ++ ++ /* get a dmac channel */ ++ ret_val = sst_alloc_dma_chan(&sst_drv_ctx->dma); ++ if (ret_val) ++ return ret_val; ++ ++ /* allocate desc for transfer and submit */ ++ ret_val = sst_dma_firmware(&sst_drv_ctx->dma, sg_list); ++ ++ sst_dma_free_resources(&sst_drv_ctx->dma); ++ ++ return ret_val; ++} ++ ++/* ++ * sst_fill_memcpy_list - Fill the memcpy list ++ * ++ * @memcpy_list: List to be filled ++ * @destn: Destination addr to be filled in the list ++ * @src: Source addr to be filled in the list ++ * @size: Size to be filled in the list ++ * ++ * Adds the node to the list after required fields ++ * are populated in the node ++ */ ++ ++static int sst_fill_memcpy_list(struct list_head *memcpy_list, ++ void *destn, const void *src, u32 size, bool is_io) ++{ ++ struct sst_memcpy_list *listnode; ++ ++ listnode = kzalloc(sizeof(*listnode), GFP_KERNEL); ++ if (listnode == NULL) ++ return -ENOMEM; ++ listnode->dstn = destn; ++ listnode->src = src; ++ listnode->size = size; ++ listnode->is_io = is_io; ++ list_add_tail(&listnode->memcpylist, memcpy_list); ++ ++ return 0; ++} ++ ++static int sst_parse_elf_module_memcpy(struct intel_sst_drv *sst, ++ const void *fw, struct sst_info info, Elf32_Phdr *pr, ++ struct list_head *memcpy_list) ++{ ++ void *dstn; ++ unsigned int dstn_phys; ++ int ret_val = 0; ++ int mem_type; ++ ++ ret_val = sst_fill_dstn(sst, info, pr, &dstn, &dstn_phys, &mem_type); ++ if (ret_val) ++ return ret_val; ++ ++ ret_val = sst_fill_memcpy_list(memcpy_list, dstn, ++ (void *)fw + pr->p_offset, pr->p_filesz, mem_type); ++ if (ret_val) ++ return ret_val; ++ ++ return 0; ++} ++ ++static int ++sst_parse_elf_fw_memcpy(struct intel_sst_drv *sst, const void *fw_in_mem, ++ struct list_head *memcpy_list) ++{ ++ int i = 0; ++ ++ Elf32_Ehdr *elf; ++ Elf32_Phdr *pr; ++ struct sst_info info; ++ ++ BUG_ON(!fw_in_mem); ++ ++ elf = (Elf32_Ehdr *)fw_in_mem; ++ pr = (Elf32_Phdr *) (fw_in_mem + elf->e_phoff); ++ pr_debug("%s entry\n", __func__); ++ ++ sst_fill_info(sst, &info); ++ ++ while (i < elf->e_phnum) { ++ if (pr[i].p_type == PT_LOAD) ++ sst_parse_elf_module_memcpy(sst, fw_in_mem, info, ++ &pr[i], memcpy_list); ++ i++; ++ } ++ return 0; ++} ++ ++/** ++ * sst_parse_module_memcpy - Parse audio FW modules and populate the memcpy list ++ * ++ * @module : FW module header ++ * @memcpy_list : Pointer to the list to be populated ++ * Create the memcpy list as the number of block to be copied ++ * returns error or 0 if module sizes are proper ++ */ ++static int sst_parse_module_memcpy(struct fw_module_header *module, ++ struct list_head *memcpy_list) ++{ ++ struct fw_block_info *block; ++ u32 count; ++ int ret_val = 0; ++ void __iomem *ram_iomem; ++ ++ pr_debug("module sign %s size %x blocks %x type %x\n", ++ module->signature, module->mod_size, ++ module->blocks, module->type); ++ pr_debug("module entrypoint 0x%x\n", module->entry_point); ++ ++ block = (void *)module + sizeof(*module); ++ ++ for (count = 0; count < module->blocks; count++) { ++ if (block->size <= 0) { ++ pr_err("block size invalid\n"); ++ return -EINVAL; ++ } ++ switch (block->type) { ++ case SST_IRAM: ++ ram_iomem = sst_drv_ctx->iram; ++ break; ++ case SST_DRAM: ++ ram_iomem = sst_drv_ctx->dram; ++ break; ++ default: ++ pr_err("wrong ram type0x%x in block0x%x\n", ++ block->type, count); ++ return -EINVAL; ++ } ++ ++ ret_val = sst_fill_memcpy_list(memcpy_list, ++ ram_iomem + block->ram_offset, ++ (void *)block + sizeof(*block), block->size, 1); ++ if (ret_val) ++ return ret_val; ++ ++ block = (void *)block + sizeof(*block) + block->size; ++ } ++ return 0; ++} ++ ++/** ++ * sst_parse_fw_memcpy - parse the firmware image & populate the list for memcpy ++ * ++ * @sst_fw_in_mem : pointer to audio fw ++ * @size : size of the firmware ++ * @fw_list : pointer to list_head to be populated ++ * This function parses the FW image and saves the parsed image in the list ++ * for memcpy ++ */ ++static int sst_parse_fw_memcpy(const void *sst_fw_in_mem, unsigned long size, ++ struct list_head *fw_list) ++{ ++ struct fw_module_header *module; ++ u32 count, num_modules; ++ int ret_val; ++ ++ ret_val = sst_validate_fw_image(sst_fw_in_mem, size, ++ &module, &num_modules); ++ if (ret_val) ++ return ret_val; ++ ++ for (count = 0; count < num_modules; count++) { ++ /* module */ ++ ret_val = sst_parse_module_memcpy(module, fw_list); ++ if (ret_val) ++ return ret_val; ++ module = (void *)module + sizeof(*module) + module->mod_size ; ++ } ++ ++ return 0; ++} ++ ++/** ++ * sst_do_memcpy - function initiates the memcpy ++ * ++ * @memcpy_list: Pter to memcpy list on which the memcpy needs to be initiated ++ * ++ * Triggers the memcpy ++ */ ++static void sst_do_memcpy(struct list_head *memcpy_list) ++{ ++ struct sst_memcpy_list *listnode; ++ ++ list_for_each_entry(listnode, memcpy_list, memcpylist) { ++ if (listnode->is_io == true) ++ MEMCPY_TOIO((void __iomem *)listnode->dstn, listnode->src, ++ listnode->size); ++ else ++ memcpy(listnode->dstn, listnode->src, listnode->size); ++ } ++} ++ ++static void sst_memcpy_free_lib_resources(void) ++{ ++ struct sst_memcpy_list *listnode, *tmplistnode; ++ ++ pr_debug("entry:%s\n", __func__); ++ ++ /*Free the list*/ ++ if (!list_empty(&sst_drv_ctx->libmemcpy_list)) { ++ list_for_each_entry_safe(listnode, tmplistnode, ++ &sst_drv_ctx->libmemcpy_list, memcpylist) { ++ list_del(&listnode->memcpylist); ++ kfree(listnode); ++ } ++ } ++} ++ ++void sst_memcpy_free_resources(void) ++{ ++ struct sst_memcpy_list *listnode, *tmplistnode; ++ ++ pr_debug("entry:%s\n", __func__); ++ ++ /*Free the list*/ ++ if (!list_empty(&sst_drv_ctx->memcpy_list)) { ++ list_for_each_entry_safe(listnode, tmplistnode, ++ &sst_drv_ctx->memcpy_list, memcpylist) { ++ list_del(&listnode->memcpylist); ++ kfree(listnode); ++ } ++ } ++ sst_memcpy_free_lib_resources(); ++} ++ ++void sst_firmware_load_cb(const struct firmware *fw, void *context) ++{ ++ struct intel_sst_drv *ctx = context; ++ int ret = 0; ++ ++ pr_debug("In %s\n", __func__); ++ ++ if (fw == NULL) { ++ pr_err("request fw failed\n"); ++ goto out; ++ } ++ ++ if (sst_drv_ctx->sst_state != SST_UN_INIT || ++ ctx->fw_in_mem != NULL) ++ goto exit; ++ ++ pr_debug("Request Fw completed\n"); ++ trace_sst_fw_download("End of FW request", ctx->sst_state); ++ ++ if (ctx->info.use_elf == true) ++ ret = sst_validate_elf(fw, false); ++ ++ if (ret != 0) { ++ pr_err("FW image invalid...\n"); ++ goto out; ++ } ++ ++ ctx->fw_in_mem = kzalloc(fw->size, GFP_KERNEL); ++ if (!ctx->fw_in_mem) { ++ pr_err("%s unable to allocate memory\n", __func__); ++ goto out; ++ } ++ ++ pr_debug("copied fw to %p", ctx->fw_in_mem); ++ pr_debug("phys: %lx", (unsigned long)virt_to_phys(ctx->fw_in_mem)); ++ memcpy(ctx->fw_in_mem, fw->data, fw->size); ++ ++ trace_sst_fw_download("Start FW parsing", ctx->sst_state); ++ if (ctx->use_dma) { ++ if (ctx->info.use_elf == true) ++ ret = sst_parse_elf_fw_dma(ctx, ctx->fw_in_mem, ++ &ctx->fw_sg_list); ++ else ++ ret = sst_parse_fw_dma(ctx->fw_in_mem, fw->size, ++ &ctx->fw_sg_list); ++ } else { ++ if (ctx->info.use_elf == true) ++ ret = sst_parse_elf_fw_memcpy(ctx, ctx->fw_in_mem, ++ &ctx->memcpy_list); ++ else ++ ret = sst_parse_fw_memcpy(ctx->fw_in_mem, fw->size, ++ &ctx->memcpy_list); ++ } ++ trace_sst_fw_download("End FW parsing", ctx->sst_state); ++ if (ret) { ++ kfree(ctx->fw_in_mem); ++ ctx->fw_in_mem = NULL; ++ goto out; ++ } ++ ++ /* If static module download(download at boot time) is supported, ++ * set the flag to indicate lib download is to be done ++ */ ++ if (ctx->pdata->lib_info) ++ if (ctx->pdata->lib_info->mod_ddr_dnld) ++ ctx->lib_dwnld_reqd = true; ++ ++ sst_set_fw_state_locked(sst_drv_ctx, SST_FW_LIB_LOAD); ++ goto exit; ++out: ++ sst_set_fw_state_locked(sst_drv_ctx, SST_UN_INIT); ++exit: ++ if (fw != NULL) ++ release_firmware(fw); ++} ++ ++/* ++ * sst_request_fw - requests audio fw from kernel and saves a copy ++ * ++ * This function requests the SST FW from the kernel, parses it and ++ * saves a copy in the driver context ++ */ ++static int sst_request_fw(struct intel_sst_drv *sst) ++{ ++ int retval = 0; ++ char name[20]; ++ const struct firmware *fw; ++ ++ snprintf(name, sizeof(name), "%s%04x%s", "fw_sst_", ++ sst->pci_id, ".bin"); ++ pr_debug("Requesting FW %s now...\n", name); ++ ++ retval = request_firmware(&fw, name, sst->dev); ++ if (fw == NULL) { ++ pr_err("fw is returning as null\n"); ++ return -EINVAL; ++ } ++ if (retval) { ++ pr_err("request fw failed %d\n", retval); ++ return retval; ++ } ++ trace_sst_fw_download("End of FW request", sst->sst_state); ++ if (sst->info.use_elf == true) ++ retval = sst_validate_elf(fw, false); ++ if (retval != 0) { ++ pr_err("FW image invalid...\n"); ++ goto end_release; ++ } ++ sst->fw_in_mem = kzalloc(fw->size, GFP_KERNEL); ++ if (!sst->fw_in_mem) { ++ pr_err("%s unable to allocate memory\n", __func__); ++ retval = -ENOMEM; ++ goto end_release; ++ } ++ pr_debug("copied fw to %p", sst->fw_in_mem); ++ pr_debug("phys: %lx", (unsigned long)virt_to_phys(sst->fw_in_mem)); ++ memcpy(sst->fw_in_mem, fw->data, fw->size); ++ trace_sst_fw_download("Start FW parsing", sst->sst_state); ++ if (sst->use_dma) { ++ if (sst->info.use_elf == true) ++ retval = sst_parse_elf_fw_dma(sst, sst->fw_in_mem, ++ &sst->fw_sg_list); ++ else ++ retval = sst_parse_fw_dma(sst->fw_in_mem, fw->size, ++ &sst->fw_sg_list); ++ } else { ++ if (sst->info.use_elf == true) ++ retval = sst_parse_elf_fw_memcpy(sst, sst->fw_in_mem, ++ &sst->memcpy_list); ++ else ++ retval = sst_parse_fw_memcpy(sst->fw_in_mem, fw->size, ++ &sst->memcpy_list); ++ } ++ trace_sst_fw_download("End FW parsing", sst->sst_state); ++ if (retval) { ++ kfree(sst->fw_in_mem); ++ sst->fw_in_mem = NULL; ++ } ++ ++ /* If static module download(download at boot time) is supported, ++ * set the flag to indicate lib download is to be done ++ */ ++ if (sst->pdata->lib_info) ++ if (sst->pdata->lib_info->mod_ddr_dnld) ++ sst->lib_dwnld_reqd = true; ++end_release: ++ release_firmware(fw); ++ return retval; ++} ++ ++static inline void print_lib_info(struct snd_sst_lib_download_info *resp) ++{ ++ pr_debug("codec Type %d Ver %d Built %s: %s\n", ++ resp->dload_lib.lib_info.lib_type, ++ resp->dload_lib.lib_info.lib_version, ++ resp->dload_lib.lib_info.b_date, ++ resp->dload_lib.lib_info.b_time); ++} ++ ++/* sst_download_library - This function is called when any ++ codec/post processing library needs to be downloaded */ ++static int sst_download_library(const struct firmware *fw_lib, ++ struct snd_sst_lib_download_info *lib) ++{ ++ int ret_val = 0; ++ ++ /* send IPC message and wait */ ++ u8 pvt_id; ++ struct ipc_post *msg = NULL; ++ union config_status_reg csr; ++ struct snd_sst_str_type str_type = {0}; ++ int retval = 0; ++ void *codec_fw; ++ struct sst_block *block; ++ ++ pvt_id = sst_assign_pvt_id(sst_drv_ctx); ++ ret_val = sst_create_block_and_ipc_msg(&msg, true, sst_drv_ctx, &block, ++ IPC_IA_PREP_LIB_DNLD, pvt_id); ++ if (ret_val) { ++ pr_err("library download failed\n"); ++ return ret_val; ++ } ++ ++ sst_fill_header(&msg->header, IPC_IA_PREP_LIB_DNLD, 1, pvt_id); ++ msg->header.part.data = sizeof(u32) + sizeof(str_type); ++ str_type.codec_type = lib->dload_lib.lib_info.lib_type; ++ /*str_type.pvt_id = pvt_id;*/ ++ memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); ++ memcpy(msg->mailbox_data + sizeof(u32), &str_type, sizeof(str_type)); ++ sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); ++ retval = sst_wait_timeout(sst_drv_ctx, block); ++ if (block->data) { ++ struct snd_sst_str_type *str_type = ++ (struct snd_sst_str_type *)block->data; ++ if (str_type->result) { ++ /* error */ ++ pr_err("Prep codec downloaded failed %d\n", ++ str_type->result); ++ retval = -EIO; ++ goto free_block; ++ } ++ kfree(block->data); ++ } else if (retval != 0) { ++ retval = -EIO; ++ goto free_block; ++ } ++ pr_debug("FW responded, ready for download now...\n"); ++ codec_fw = kzalloc(fw_lib->size, GFP_KERNEL); ++ if (!codec_fw) { ++ memset(lib, 0, sizeof(*lib)); ++ retval = -ENOMEM; ++ goto send_ipc; ++ } ++ memcpy(codec_fw, fw_lib->data, fw_lib->size); ++ ++ if (sst_drv_ctx->use_dma) ++ retval = sst_parse_fw_dma(codec_fw, fw_lib->size, ++ &sst_drv_ctx->library_list); ++ else ++ retval = sst_parse_fw_memcpy(codec_fw, fw_lib->size, ++ &sst_drv_ctx->libmemcpy_list); ++ ++ if (retval) { ++ memset(lib, 0, sizeof(*lib)); ++ goto send_ipc; ++ } ++ ++ /* downloading on success */ ++ mutex_lock(&sst_drv_ctx->sst_lock); ++ sst_drv_ctx->sst_state = SST_FW_LOADED; ++ mutex_lock(&sst_drv_ctx->csr_lock); ++ csr.full = readl(sst_drv_ctx->shim + SST_CSR); ++ csr.part.run_stall = 1; ++ sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); ++ ++ csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); ++ csr.part.bypass = 0x7; ++ sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); ++ mutex_unlock(&sst_drv_ctx->csr_lock); ++ ++ if (sst_drv_ctx->use_dma) { ++ ret_val = sst_do_dma(&sst_drv_ctx->library_list); ++ if (ret_val) { ++ pr_err("sst_do_dma failed, abort\n"); ++ memset(lib, 0, sizeof(*lib)); ++ } ++ } else ++ sst_do_memcpy(&sst_drv_ctx->libmemcpy_list); ++ /* set the FW to running again */ ++ mutex_lock(&sst_drv_ctx->csr_lock); ++ csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); ++ csr.part.bypass = 0x0; ++ sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); ++ ++ csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); ++ csr.part.run_stall = 0; ++ sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); ++ mutex_unlock(&sst_drv_ctx->csr_lock); ++send_ipc: ++ /* send download complete and wait */ ++ if (sst_create_ipc_msg(&msg, true)) { ++ retval = -ENOMEM; ++ goto free_resources; ++ } ++ ++ block->condition = false; ++ block->msg_id = IPC_IA_LIB_DNLD_CMPLT; ++ sst_fill_header(&msg->header, IPC_IA_LIB_DNLD_CMPLT, 1, pvt_id); ++ msg->header.part.data = sizeof(u32) + sizeof(*lib); ++ lib->pvt_id = pvt_id; ++ memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); ++ memcpy(msg->mailbox_data + sizeof(u32), lib, sizeof(*lib)); ++ sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); ++ pr_debug("Waiting for FW response Download complete\n"); ++ retval = sst_wait_timeout(sst_drv_ctx, block); ++ sst_drv_ctx->sst_state = SST_FW_RUNNING; ++ if (block->data) { ++ struct snd_sst_lib_download_info *resp = block->data; ++ retval = resp->result; ++ if (retval) { ++ pr_err("err in lib dload %x\n", resp->result); ++ goto free_resources; ++ } else { ++ pr_debug("Codec download complete...\n"); ++ print_lib_info(resp); ++ } ++ } else if (retval) { ++ /* error */ ++ retval = -EIO; ++ goto free_resources; ++ } ++ ++ pr_debug("FW success on Download complete\n"); ++ ++free_resources: ++ if (sst_drv_ctx->use_dma) { ++ kfree(sst_drv_ctx->library_list.src); ++ kfree(sst_drv_ctx->library_list.dst); ++ sst_drv_ctx->library_list.list_len = 0; ++ } ++ ++ kfree(codec_fw); ++ mutex_unlock(&sst_drv_ctx->sst_lock); ++free_block: ++ sst_free_block(sst_drv_ctx, block); ++ return retval; ++} ++ ++/* ++ * Writing the DDR physical base to DCCM offset ++ * so that FW can use it to setup TLB ++ */ ++static void sst_dccm_config_write(void __iomem *dram_base, unsigned int ddr_base) ++{ ++ void __iomem *addr; ++ u32 bss_reset = 0; ++ ++ addr = (void __iomem *)(dram_base + MRFLD_FW_DDR_BASE_OFFSET); ++ MEMCPY_TOIO(addr, (void *)&ddr_base, sizeof(u32)); ++ bss_reset |= (1 << MRFLD_FW_BSS_RESET_BIT); ++ addr = (void __iomem *)(dram_base + MRFLD_FW_FEATURE_BASE_OFFSET); ++ MEMCPY_TOIO(addr, &bss_reset, sizeof(u32)); ++ pr_debug("%s: config written to DCCM\n", __func__); ++} ++ ++void sst_post_download_mrfld(struct intel_sst_drv *ctx) ++{ ++ sst_dccm_config_write(ctx->dram, ctx->ddr_base); ++ /* For mrfld, download all libraries the first time fw is ++ * downloaded */ ++ pr_debug("%s: lib_dwnld = %u\n", __func__, ctx->lib_dwnld_reqd); ++ if (ctx->lib_dwnld_reqd) { ++ sst_load_all_modules_elf(ctx, sst_modules_mrfld, ARRAY_SIZE(sst_modules_mrfld)); ++ ctx->lib_dwnld_reqd = false; ++ } ++} ++ ++void sst_post_download_ctp(struct intel_sst_drv *ctx) ++{ ++ sst_fill_config(ctx, 0); ++} ++ ++void sst_post_download_byt(struct intel_sst_drv *ctx) ++{ ++ sst_dccm_config_write(ctx->dram, ctx->ddr_base); ++ sst_fill_config(ctx, 2 * sizeof(u32)); ++ ++ pr_debug("%s: lib_dwnld = %u\n", __func__, ctx->lib_dwnld_reqd); ++ if (ctx->lib_dwnld_reqd) { ++ sst_load_all_modules_elf(ctx, sst_modules_byt, ++ ARRAY_SIZE(sst_modules_byt)); ++ ctx->lib_dwnld_reqd = false; ++ } ++} ++ ++static void sst_init_lib_mem_mgr(struct intel_sst_drv *ctx) ++{ ++ struct sst_mem_mgr *mgr = &ctx->lib_mem_mgr; ++ const struct sst_lib_dnld_info *lib_info = ctx->pdata->lib_info; ++ ++ memset(mgr, 0, sizeof(*mgr)); ++ mgr->current_base = lib_info->mod_base + lib_info->mod_table_offset ++ + lib_info->mod_table_size; ++ mgr->avail = lib_info->mod_end - mgr->current_base + 1; ++ ++ pr_debug("current base = 0x%lx , avail = 0x%x\n", ++ (unsigned long)mgr->current_base, mgr->avail); ++} ++ ++/** ++ * sst_load_fw - function to load FW into DSP ++ * ++ * ++ * Transfers the FW to DSP using dma/memcpy ++ */ ++int sst_load_fw(void) ++{ ++ int ret_val = 0; ++ struct sst_block *block; ++ ++ pr_debug("sst_load_fw\n"); ++ ++ if ((sst_drv_ctx->sst_state != SST_START_INIT && ++ sst_drv_ctx->sst_state != SST_FW_LIB_LOAD) || ++ sst_drv_ctx->sst_state == SST_SHUTDOWN) ++ return -EAGAIN; ++ ++ if (!sst_drv_ctx->fw_in_mem) { ++ if (sst_drv_ctx->sst_state != SST_START_INIT) { ++ /* even wake*/ ++ pr_err("sst : wait for FW to be downloaded\n"); ++ return -EBUSY; ++ } else { ++ trace_sst_fw_download("Req FW sent in check device", ++ sst_drv_ctx->sst_state); ++ pr_debug("sst: FW not in memory retry to download\n"); ++ ret_val = sst_request_fw(sst_drv_ctx); ++ if (ret_val) ++ return ret_val; ++ } ++ } ++ ++ BUG_ON(!sst_drv_ctx->fw_in_mem); ++ block = sst_create_block(sst_drv_ctx, 0, FW_DWNL_ID); ++ if (block == NULL) ++ return -ENOMEM; ++ ++ /* Prevent C-states beyond C6 */ ++ pm_qos_update_request(sst_drv_ctx->qos, CSTATE_EXIT_LATENCY_S0i1 - 1); ++ ++ ret_val = sst_drv_ctx->ops->reset(); ++ if (ret_val) ++ goto restore; ++ ++ trace_sst_fw_download("Start FW copy", sst_drv_ctx->sst_state); ++ if (sst_drv_ctx->use_dma) { ++ ret_val = sst_do_dma(&sst_drv_ctx->fw_sg_list); ++ if (ret_val) { ++ pr_err("sst_do_dma failed, abort\n"); ++ goto restore; ++ } ++ } else { ++ sst_do_memcpy(&sst_drv_ctx->memcpy_list); ++ } ++ ++ trace_sst_fw_download("Post download for Lib start", ++ sst_drv_ctx->sst_state); ++ /* Write the DRAM/DCCM config before enabling FW */ ++ if (sst_drv_ctx->ops->post_download) ++ sst_drv_ctx->ops->post_download(sst_drv_ctx); ++ trace_sst_fw_download("Post download for Lib end", ++ sst_drv_ctx->sst_state); ++ sst_drv_ctx->sst_state = SST_FW_LOADED; ++ ++ /* bring sst out of reset */ ++ ret_val = sst_drv_ctx->ops->start(); ++ if (ret_val) ++ goto restore; ++ trace_sst_fw_download("DSP reset done", ++ sst_drv_ctx->sst_state); ++ ++ ret_val = sst_wait_timeout(sst_drv_ctx, block); ++ if (ret_val) { ++ pr_err("fw download failed %d\n" , ret_val); ++ /* assume FW d/l failed due to timeout*/ ++ ret_val = -EBUSY; ++ ++ } ++ ++restore: ++ /* Re-enable Deeper C-states beyond C6 */ ++ pm_qos_update_request(sst_drv_ctx->qos, PM_QOS_DEFAULT_VALUE); ++ sst_free_block(sst_drv_ctx, block); ++ ++ return ret_val; ++} ++ ++/** ++ * sst_load_library - function to load FW into DSP ++ * ++ * @lib: Pointer to the lib download structure ++ * @ops: Contains the stream ops ++ * This function is called when FW requests for a particular library download ++ * This function prepares & downloads the library ++ */ ++int sst_load_library(struct snd_sst_lib_download *lib, u8 ops) ++{ ++ char buf[20]; ++ const char *type, *dir; ++ int len = 0, error = 0; ++ u32 entry_point; ++ const struct firmware *fw_lib; ++ struct snd_sst_lib_download_info dload_info = {{{0},},}; ++ ++ memset(buf, 0, sizeof(buf)); ++ ++ pr_debug("Lib Type 0x%x, Slot 0x%x, ops 0x%x\n", ++ lib->lib_info.lib_type, lib->slot_info.slot_num, ops); ++ pr_debug("Version 0x%x, name %s, caps 0x%x media type 0x%x\n", ++ lib->lib_info.lib_version, lib->lib_info.lib_name, ++ lib->lib_info.lib_caps, lib->lib_info.media_type); ++ ++ pr_debug("IRAM Size 0x%x, offset 0x%x\n", ++ lib->slot_info.iram_size, lib->slot_info.iram_offset); ++ pr_debug("DRAM Size 0x%x, offset 0x%x\n", ++ lib->slot_info.dram_size, lib->slot_info.dram_offset); ++ ++ switch (lib->lib_info.lib_type) { ++ case SST_CODEC_TYPE_MP3: ++ type = "mp3_"; ++ break; ++ case SST_CODEC_TYPE_AAC: ++ type = "aac_"; ++ break; ++ case SST_CODEC_TYPE_AACP: ++ type = "aac_v1_"; ++ break; ++ case SST_CODEC_TYPE_eAACP: ++ type = "aac_v2_"; ++ break; ++ case SST_CODEC_TYPE_WMA9: ++ type = "wma9_"; ++ break; ++ default: ++ pr_err("Invalid codec type\n"); ++ error = -EINVAL; ++ goto wake; ++ } ++ ++ if (ops == STREAM_OPS_CAPTURE) ++ dir = "enc_"; ++ else ++ dir = "dec_"; ++ len = strlen(type) + strlen(dir); ++ strncpy(buf, type, sizeof(buf)-1); ++ strncpy(buf + strlen(type), dir, sizeof(buf)-strlen(type)-1); ++ len += snprintf(buf + len, sizeof(buf) - len, "%d", ++ lib->slot_info.slot_num); ++ len += snprintf(buf + len, sizeof(buf) - len, ".bin"); ++ ++ pr_debug("Requesting %s\n", buf); ++ ++ error = request_firmware(&fw_lib, buf, sst_drv_ctx->dev); ++ if (fw_lib == NULL) { ++ pr_err("fw_lib pointer is returning null\n"); ++ return -EINVAL; ++ } ++ if (error) { ++ pr_err("library load failed %d\n", error); ++ goto wake; ++ } ++ error = sst_validate_library(fw_lib, &lib->slot_info, &entry_point); ++ if (error) ++ goto wake_free; ++ ++ lib->mod_entry_pt = entry_point; ++ memcpy(&dload_info.dload_lib, lib, sizeof(*lib)); ++ /* Prevent C-states beyond C6 */ ++ pm_qos_update_request(sst_drv_ctx->qos, CSTATE_EXIT_LATENCY_S0i1 - 1); ++ error = sst_download_library(fw_lib, &dload_info); ++ /* Re-enable Deeper C-states beyond C6 */ ++ pm_qos_update_request(sst_drv_ctx->qos, PM_QOS_DEFAULT_VALUE); ++ if (error) ++ goto wake_free; ++ ++ /* lib is downloaded and init send alloc again */ ++ pr_debug("Library is downloaded now...\n"); ++wake_free: ++ /* sst_wake_up_alloc_block(sst_drv_ctx, pvt_id, error, NULL); */ ++ release_firmware(fw_lib); ++wake: ++ return error; ++} ++ ++/* In relocatable elf file, there can be relocatable variables and functions. ++ * Variables are kept in Global Address Offset Table (GOT) and functions in ++ * Procedural Linkage Table (PLT). In current codec binaries only relocatable ++ * variables are seen. So we use the GOT table. ++ */ ++static int sst_find_got_table(Elf32_Shdr *shdr, int nsec, char *in_elf, ++ Elf32_Rela **got, unsigned int *cnt) ++{ ++ int i = 0; ++ while (i < nsec) { ++ if (shdr[i].sh_type == SHT_RELA) { ++ *got = (Elf32_Rela *)(in_elf + shdr[i].sh_offset); ++ *cnt = shdr[i].sh_size / sizeof(Elf32_Rela); ++ break; ++ } ++ i++; ++ } ++ if (i == nsec) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++/* For each entry in the GOT table, find the unrelocated offset. Then ++ * add the relocation base to the offset and write back the new address to the ++ * original variable location. ++ */ ++static int sst_relocate_got_entries(Elf32_Rela *table, unsigned int size, ++ char *in_elf, int elf_size, u32 rel_base) ++{ ++ int i; ++ Elf32_Rela *entry; ++ Elf32_Addr *target_addr, unreloc_addr; ++ ++ for (i = 0; i < size; i++) { ++ entry = &table[i]; ++ if (ELF32_R_SYM(entry->r_info) != 0) { ++ return -EINVAL; ++ } else { ++ if (entry->r_offset > elf_size) { ++ pr_err("GOT table target addr out of range\n"); ++ return -EINVAL; ++ } ++ target_addr = (Elf32_Addr *)(in_elf + entry->r_offset); ++ unreloc_addr = *target_addr + entry->r_addend; ++ if (unreloc_addr > elf_size) { ++ pr_err("GOT table entry invalid\n"); ++ continue; ++ } ++ *target_addr = unreloc_addr + rel_base; ++ } ++ } ++ return 0; ++} ++ ++static int sst_relocate_elf(char *in_elf, int elf_size, phys_addr_t rel_base, ++ Elf32_Addr *entry_pt) ++{ ++ int retval = 0; ++ Elf32_Ehdr *ehdr = (Elf32_Ehdr *)in_elf; ++ Elf32_Shdr *shdr = (Elf32_Shdr *) (in_elf + ehdr->e_shoff); ++ Elf32_Phdr *phdr = (Elf32_Phdr *) (in_elf + ehdr->e_phoff); ++ int i, num_sec; ++ Elf32_Rela *rel_table = NULL; ++ unsigned int rela_cnt = 0; ++ u32 rbase; ++ ++ BUG_ON(rel_base > (u32)(-1)); ++ rbase = (u32) (rel_base & (u32)(~0)); ++ ++ /* relocate the entry_pt */ ++ *entry_pt = (Elf32_Addr)(ehdr->e_entry + rbase); ++ num_sec = ehdr->e_shnum; ++ ++ /* Find the relocation(GOT) table through the section header */ ++ retval = sst_find_got_table(shdr, num_sec, in_elf, ++ &rel_table, &rela_cnt); ++ if (retval < 0) ++ return retval; ++ ++ /* Relocate all the entries in the GOT */ ++ retval = sst_relocate_got_entries(rel_table, rela_cnt, in_elf, ++ elf_size, rbase); ++ if (retval < 0) ++ return retval; ++ ++ pr_debug("GOT entries relocated\n"); ++ ++ /* Update the program headers in the ELF */ ++ for (i = 0; i < ehdr->e_phnum; i++) { ++ if (phdr[i].p_type == PT_LOAD) { ++ phdr[i].p_vaddr += rbase; ++ phdr[i].p_paddr += rbase; ++ } ++ } ++ pr_debug("program header entries updated\n"); ++ ++ return retval; ++} ++ ++#define ALIGN_256 0x100 ++ ++int sst_get_next_lib_mem(struct sst_mem_mgr *mgr, int size, ++ unsigned long *lib_base) ++{ ++ int retval = 0; ++ ++ pr_debug("library orig size = 0x%x", size); ++ if (size % ALIGN_256) ++ size += (ALIGN_256 - (size % ALIGN_256)); ++ if (size > mgr->avail) ++ return -ENOMEM; ++ ++ *lib_base = mgr->current_base; ++ mgr->current_base += size; ++ mgr->avail -= size; ++ mgr->count++; ++ pr_debug("library base = 0x%lx", *lib_base); ++ pr_debug("library aligned size = 0x%x", size); ++ pr_debug("lib count = %d\n", mgr->count); ++ return retval; ++ ++} ++ ++static int sst_download_lib_elf(struct intel_sst_drv *sst, const void *lib, ++ int size) ++{ ++ int retval = 0; ++ ++ pr_debug("In %s\n", __func__); ++ ++ if (sst->use_dma) { ++ retval = sst_parse_elf_fw_dma(sst, lib, ++ &sst->library_list); ++ if (retval) ++ goto free_dma_res; ++ retval = sst_do_dma(&sst->library_list); ++ if (retval) ++ pr_err("sst_do_dma failed, abort\n"); ++free_dma_res: ++ kfree(sst->library_list.src); ++ kfree(sst->library_list.dst); ++ sst->library_list.list_len = 0; ++ } else { ++ retval = sst_parse_elf_fw_memcpy(sst, lib, ++ &sst->libmemcpy_list); ++ if (retval) ++ return retval; ++ sst_do_memcpy(&sst->libmemcpy_list); ++ sst_memcpy_free_lib_resources(); ++ } ++ pr_debug("download lib complete"); ++ return retval; ++} ++ ++static void sst_fill_fw_module_table(struct sst_module_info *mod_list, ++ int list_size, unsigned long ddr_base) ++{ ++ int i; ++ u32 *write_ptr = (u32 *)ddr_base; ++ ++ pr_debug("In %s\n", __func__); ++ ++ for (i = 0; i < list_size; i++) { ++ if (mod_list[i].status == SST_LIB_DOWNLOADED) { ++ pr_debug("status dnwld for %d\n", i); ++ pr_debug("module id %d\n", mod_list[i].id); ++ pr_debug("entry pt 0x%x\n", mod_list[i].entry_pt); ++ ++ *write_ptr++ = mod_list[i].id; ++ *write_ptr++ = mod_list[i].entry_pt; ++ } ++ } ++} ++ ++static int sst_request_lib_elf(struct sst_module_info *mod_entry, ++ const struct firmware **fw_lib, int pci_id, struct device *dev) ++{ ++ char name[25]; ++ int retval = 0; ++ ++ snprintf(name, sizeof(name), "%s%s%04x%s", mod_entry->name, ++ "_", pci_id, ".bin"); ++ pr_debug("Requesting %s\n", name); ++ ++ retval = request_firmware(fw_lib, name, dev); ++ if (retval) { ++ pr_err("%s library load failed %d\n", name, retval); ++ return retval; ++ } ++ pr_debug("got lib\n"); ++ mod_entry->status = SST_LIB_FOUND; ++ return 0; ++} ++ ++static int sst_allocate_lib_mem(const struct firmware *lib, int size, ++ struct sst_mem_mgr *mem_mgr, char **out_elf, unsigned long *lib_start) ++{ ++ int retval = 0; ++ ++ *out_elf = kzalloc(size, GFP_KERNEL); ++ if (!*out_elf) { ++ pr_err("cannot alloc mem for elf copy %d\n", retval); ++ goto mem_error; ++ } ++ ++ memcpy(*out_elf, lib->data, size); ++ retval = sst_get_next_lib_mem(mem_mgr, size, lib_start); ++ if (retval < 0) { ++ pr_err("cannot alloc ddr mem for lib: %d\n", retval); ++ kfree(*out_elf); ++ goto mem_error; ++ } ++ return 0; ++ ++mem_error: ++ release_firmware(lib); ++ return -ENOMEM; ++} ++ ++int sst_load_all_modules_elf(struct intel_sst_drv *ctx, struct sst_module_info *mod_table, ++ int num_modules) ++{ ++ int retval = 0; ++ int i; ++ const struct firmware *fw_lib; ++ struct sst_module_info *mod = NULL; ++ char *out_elf; ++ unsigned int lib_size = 0; ++ unsigned int mod_table_offset = ctx->pdata->lib_info->mod_table_offset; ++ unsigned long lib_base; ++ ++ pr_debug("In %s", __func__); ++ ++ sst_init_lib_mem_mgr(ctx); ++ ++ for (i = 0; i < num_modules; i++) { ++ mod = &mod_table[i]; ++ trace_sst_lib_download("Start of Request Lib", mod->name); ++ retval = sst_request_lib_elf(mod, &fw_lib, ++ ctx->pci_id, ctx->dev); ++ if (retval < 0) ++ continue; ++ lib_size = fw_lib->size; ++ ++ trace_sst_lib_download("End of Request Lib", mod->name); ++ retval = sst_validate_elf(fw_lib, true); ++ if (retval < 0) { ++ pr_err("library is not valid elf %d\n", retval); ++ release_firmware(fw_lib); ++ continue; ++ } ++ pr_debug("elf validated\n"); ++ retval = sst_allocate_lib_mem(fw_lib, lib_size, ++ &ctx->lib_mem_mgr, &out_elf, &lib_base); ++ if (retval < 0) { ++ pr_err("lib mem allocation failed: %d\n", retval); ++ continue; ++ } ++ pr_debug("lib space allocated\n"); ++ ++ /* relocate in place */ ++ retval = sst_relocate_elf(out_elf, lib_size, ++ lib_base, &mod->entry_pt); ++ if (retval < 0) { ++ pr_err("lib elf relocation failed: %d\n", retval); ++ release_firmware(fw_lib); ++ kfree(out_elf); ++ continue; ++ } ++ pr_debug("relocation done\n"); ++ release_firmware(fw_lib); ++ trace_sst_lib_download("Start of download Lib", mod->name); ++ /* write to ddr imr region,use memcpy method */ ++ retval = sst_download_lib_elf(ctx, out_elf, lib_size); ++ trace_sst_lib_download("End of download Lib", mod->name); ++ mod->status = SST_LIB_DOWNLOADED; ++ kfree(out_elf); ++ } ++ ++ /* write module table to DDR */ ++ sst_fill_fw_module_table(mod_table, num_modules, ++ (unsigned long)(ctx->ddr + mod_table_offset)); ++ return retval; ++} +diff --git a/sound/soc/intel/sst/sst_ipc.c b/sound/soc/intel/sst/sst_ipc.c +new file mode 100644 +index 0000000..097c715 +--- /dev/null ++++ b/sound/soc/intel/sst/sst_ipc.c +@@ -0,0 +1,768 @@ ++/* ++ * sst_ipc.c - Intel SST Driver for audio engine ++ * ++ * Copyright (C) 2008-10 Intel Corporation ++ * Authors: Vinod Koul ++ * Harsha Priya ++ * Dharageswari R ++ * KP Jeeja ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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. ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * This file defines all ipc functions ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include "../sst_platform.h" ++#include "../platform_ipc_v2.h" ++#include "sst.h" ++#include "sst_trace.h" ++ ++void sst_dump_to_buffer(const void *from, size_t len, char *buf) ++{ ++ int i, end; ++ const unsigned char *cmd = from; ++ ++ if (len == 0) { ++ buf[0] = '\0'; ++ return; ++ } ++ ++ for (end = len - 1; end >= 0; end--) ++ if (cmd[end]) ++ break; ++ end++; ++ ++ buf += snprintf(buf, 3, "%02x", cmd[0]); ++ for (i = 1; i < len; i++) { ++ buf += snprintf(buf, 4, " %02x", cmd[i]); ++ if (i == end && end != len - 1) { ++ sprintf(buf, "..."); ++ break; ++ } ++ } ++} ++ ++struct sst_block *sst_create_block(struct intel_sst_drv *ctx, ++ u32 msg_id, u32 drv_id) ++{ ++ struct sst_block *msg = NULL; ++ ++ pr_debug("in %s\n", __func__); ++ msg = kzalloc(sizeof(*msg), GFP_KERNEL); ++ if (!msg) { ++ pr_err("kzalloc block failed\n"); ++ return NULL; ++ } ++ msg->condition = false; ++ msg->on = true; ++ msg->msg_id = msg_id; ++ msg->drv_id = drv_id; ++ spin_lock_bh(&ctx->block_lock); ++ list_add_tail(&msg->node, &ctx->block_list); ++ spin_unlock_bh(&ctx->block_lock); ++ ++ return msg; ++} ++ ++int sst_wake_up_block(struct intel_sst_drv *ctx, int result, ++ u32 drv_id, u32 ipc, void *data, u32 size) ++{ ++ struct sst_block *block = NULL; ++ ++ pr_debug("in %s\n", __func__); ++ spin_lock_bh(&ctx->block_lock); ++ list_for_each_entry(block, &ctx->block_list, node) { ++ pr_debug("Block ipc %d, drv_id %d\n", block->msg_id, ++ block->drv_id); ++ if (block->msg_id == ipc && block->drv_id == drv_id) { ++ pr_debug("free up the block\n"); ++ block->ret_code = result; ++ block->data = data; ++ block->size = size; ++ block->condition = true; ++ spin_unlock_bh(&ctx->block_lock); ++ wake_up(&ctx->wait_queue); ++ return 0; ++ } ++ } ++ spin_unlock_bh(&ctx->block_lock); ++ pr_debug("Block not found or a response is received for a short message for ipc %d, drv_id %d\n", ++ ipc, drv_id); ++ return -EINVAL; ++} ++ ++int sst_free_block(struct intel_sst_drv *ctx, struct sst_block *freed) ++{ ++ struct sst_block *block = NULL, *__block; ++ ++ pr_debug("in %s\n", __func__); ++ spin_lock_bh(&ctx->block_lock); ++ list_for_each_entry_safe(block, __block, &ctx->block_list, node) { ++ if (block == freed) { ++ list_del(&freed->node); ++ kfree(freed->data); ++ freed->data = NULL; ++ kfree(freed); ++ spin_unlock_bh(&ctx->block_lock); ++ return 0; ++ } ++ } ++ spin_unlock_bh(&ctx->block_lock); ++ return -EINVAL; ++} ++ ++/* ++ * sst_send_runtime_param - send runtime param to SST ++ * ++ * this function sends the runtime parameter to sst dsp engine ++ */ ++static int sst_send_runtime_param(struct snd_sst_runtime_params *params) ++{ ++ struct ipc_post *msg = NULL; ++ int ret_val; ++ ++ pr_debug("Enter:%s\n", __func__); ++ ret_val = sst_create_ipc_msg(&msg, true); ++ if (ret_val) ++ return ret_val; ++ sst_fill_header(&msg->header, IPC_IA_SET_RUNTIME_PARAMS, 1, ++ params->str_id); ++ msg->header.part.data = sizeof(u32) + sizeof(*params) - sizeof(params->addr) ++ + params->size; ++ memcpy(msg->mailbox_data, &msg->header.full, sizeof(u32)); ++ memcpy(msg->mailbox_data + sizeof(u32), params, sizeof(*params) ++ - sizeof(params->addr)); ++ /* driver doesn't need to send address, so overwrite addr with data */ ++ memcpy(msg->mailbox_data + sizeof(u32) + sizeof(*params) ++ - sizeof(params->addr), ++ params->addr, params->size); ++ sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); ++ return 0; ++} ++ ++void sst_post_message_mrfld(struct work_struct *work) ++{ ++ struct ipc_post *msg; ++ union ipc_header_mrfld header; ++ unsigned long irq_flags; ++ ++ pr_debug("Enter:%s\n", __func__); ++ spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags); ++ /* check list */ ++ if (list_empty(&sst_drv_ctx->ipc_dispatch_list)) { ++ /* queue is empty, nothing to send */ ++ spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); ++ pr_debug("Empty msg queue... NO Action\n"); ++ return; ++ } ++ ++ /* check busy bit */ ++ header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX); ++ if (header.p.header_high.part.busy) { ++ spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); ++ pr_debug("Busy not free... post later\n"); ++ return; ++ } ++ /* copy msg from list */ ++ msg = list_entry(sst_drv_ctx->ipc_dispatch_list.next, ++ struct ipc_post, node); ++ list_del(&msg->node); ++ pr_debug("sst: size: = %x\n", msg->mrfld_header.p.header_low_payload); ++ if (msg->mrfld_header.p.header_high.part.large) ++ memcpy_toio(sst_drv_ctx->mailbox + SST_MAILBOX_SEND, ++ msg->mailbox_data, msg->mrfld_header.p.header_low_payload); ++ ++ trace_sst_ipc("POST ->", msg->mrfld_header.p.header_high.full, ++ msg->mrfld_header.p.header_low_payload, ++ msg->mrfld_header.p.header_high.part.drv_id); ++ trace_sst_ipc_mailbox(msg->mailbox_data, msg->mrfld_header.p.header_low_payload); ++ sst_shim_write64(sst_drv_ctx->shim, SST_IPCX, msg->mrfld_header.full); ++ spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); ++ pr_debug("sst: Post message: header = %x\n", ++ msg->mrfld_header.p.header_high.full); ++ kfree(msg->mailbox_data); ++ kfree(msg); ++ return; ++} ++ ++/** ++* sst_post_message - Posts message to SST ++* ++* @work: Pointer to work structure ++* ++* This function is called by any component in driver which ++* wants to send an IPC message. This will post message only if ++* busy bit is free ++*/ ++void sst_post_message_mfld(struct work_struct *work) ++{ ++ struct ipc_post *msg; ++ union ipc_header header; ++ unsigned long irq_flags; ++ ++ pr_debug("Enter:%s\n", __func__); ++ ++ spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags); ++ /* check list */ ++ if (list_empty(&sst_drv_ctx->ipc_dispatch_list)) { ++ /* queue is empty, nothing to send */ ++ spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); ++ pr_debug("Empty msg queue... NO Action\n"); ++ return; ++ } ++ ++ /* check busy bit */ ++ header.full = sst_shim_read(sst_drv_ctx->shim, sst_drv_ctx->ipc_reg.ipcx); ++ if (header.part.busy) { ++ spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); ++ pr_debug("Busy not free... Post later\n"); ++ return; ++ } ++ /* copy msg from list */ ++ msg = list_entry(sst_drv_ctx->ipc_dispatch_list.next, ++ struct ipc_post, node); ++ list_del(&msg->node); ++ pr_debug("size: = %x\n", msg->header.part.data); ++ if (msg->header.part.large) ++ memcpy_toio(sst_drv_ctx->mailbox + SST_MAILBOX_SEND, ++ msg->mailbox_data, msg->header.part.data); ++ ++ sst_shim_write(sst_drv_ctx->shim, sst_drv_ctx->ipc_reg.ipcx, msg->header.full); ++ spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); ++ pr_debug("Posted message: header = %x\n", msg->header.full); ++ ++ kfree(msg->mailbox_data); ++ kfree(msg); ++ return; ++} ++ ++int sst_sync_post_message_mrfld(struct ipc_post *msg) ++{ ++ union ipc_header_mrfld header; ++ unsigned int loop_count = 0; ++ int retval = 0; ++ unsigned long irq_flags; ++ ++ pr_debug("Enter:%s\n", __func__); ++ spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags); ++ ++ /* check busy bit */ ++ header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX); ++ while (header.p.header_high.part.busy) { ++ if (loop_count > 10) { ++ pr_err("sst: Busy wait failed, cant send this msg\n"); ++ retval = -EBUSY; ++ goto out; ++ } ++ udelay(500); ++ loop_count++; ++ header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX); ++ } ++ pr_debug("sst: Post message: header = %x\n", ++ msg->mrfld_header.p.header_high.full); ++ pr_debug("sst: size = 0x%x\n", msg->mrfld_header.p.header_low_payload); ++ if (msg->mrfld_header.p.header_high.part.large) ++ memcpy_toio(sst_drv_ctx->mailbox + SST_MAILBOX_SEND, ++ msg->mailbox_data, msg->mrfld_header.p.header_low_payload); ++ ++ trace_sst_ipc("POST ->", msg->mrfld_header.p.header_high.full, ++ msg->mrfld_header.p.header_low_payload, ++ msg->mrfld_header.p.header_high.part.drv_id); ++ trace_sst_ipc_mailbox(msg->mailbox_data, msg->mrfld_header.p.header_low_payload); ++ sst_shim_write64(sst_drv_ctx->shim, SST_IPCX, msg->mrfld_header.full); ++ ++out: ++ spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); ++ kfree(msg->mailbox_data); ++ kfree(msg); ++ return retval; ++} ++ ++/* use this for trigger ops to post syncronous msgs ++ */ ++int sst_sync_post_message_mfld(struct ipc_post *msg) ++{ ++ union ipc_header header; ++ unsigned int loop_count = 0; ++ int retval = 0; ++ unsigned long irq_flags; ++ ++ pr_debug("Enter:%s\n", __func__); ++ spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags); ++ ++ /* check busy bit */ ++ header.full = sst_shim_read(sst_drv_ctx->shim, sst_drv_ctx->ipc_reg.ipcx); ++ while (header.part.busy) { ++ if (loop_count > 10) { ++ pr_err("busy wait failed, cant send this msg\n"); ++ retval = -EBUSY; ++ goto out; ++ } ++ udelay(500); ++ loop_count++; ++ header.full = sst_shim_read(sst_drv_ctx->shim, sst_drv_ctx->ipc_reg.ipcx); ++ } ++ pr_debug("sst: Post message: header = %x\n", msg->header.full); ++ if (msg->header.part.large) ++ memcpy_toio(sst_drv_ctx->mailbox + SST_MAILBOX_SEND, ++ msg->mailbox_data, msg->header.part.data); ++ sst_shim_write(sst_drv_ctx->shim, sst_drv_ctx->ipc_reg.ipcx, msg->header.full); ++ ++out: ++ spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); ++ kfree(msg->mailbox_data); ++ kfree(msg); ++ ++ return retval; ++} ++ ++/* ++ * sst_clear_interrupt - clear the SST FW interrupt ++ * ++ * This function clears the interrupt register after the interrupt ++ * bottom half is complete allowing next interrupt to arrive ++ */ ++void intel_sst_clear_intr_mfld(void) ++{ ++ union interrupt_reg isr; ++ union interrupt_reg imr; ++ union ipc_header clear_ipc; ++ unsigned long irq_flags; ++ ++ spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags); ++ imr.full = sst_shim_read(sst_drv_ctx->shim, SST_IMRX); ++ isr.full = sst_shim_read(sst_drv_ctx->shim, SST_ISRX); ++ /* write 1 to clear */; ++ isr.part.busy_interrupt = 1; ++ sst_shim_write(sst_drv_ctx->shim, SST_ISRX, isr.full); ++ /* Set IA done bit */ ++ clear_ipc.full = sst_shim_read(sst_drv_ctx->shim, sst_drv_ctx->ipc_reg.ipcd); ++ clear_ipc.part.busy = 0; ++ clear_ipc.part.done = 1; ++ clear_ipc.part.data = IPC_ACK_SUCCESS; ++ sst_shim_write(sst_drv_ctx->shim, sst_drv_ctx->ipc_reg.ipcd, clear_ipc.full); ++ /* un mask busy interrupt */ ++ imr.part.busy_interrupt = 0; ++ imr.part.done_interrupt = 0; ++ sst_shim_write(sst_drv_ctx->shim, SST_IMRX, imr.full); ++ spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); ++} ++ ++ ++void intel_sst_clear_intr_mrfld(void) ++{ ++ union interrupt_reg_mrfld isr; ++ union interrupt_reg_mrfld imr; ++ union ipc_header_mrfld clear_ipc; ++ unsigned long irq_flags; ++ ++ spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags); ++ imr.full = sst_shim_read64(sst_drv_ctx->shim, SST_IMRX); ++ isr.full = sst_shim_read64(sst_drv_ctx->shim, SST_ISRX); ++ ++ /* write 1 to clear */ ++ isr.part.busy_interrupt = 1; ++ sst_shim_write64(sst_drv_ctx->shim, SST_ISRX, isr.full); ++ ++ /* Set IA done bit */ ++ clear_ipc.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCD); ++ ++ clear_ipc.p.header_high.part.busy = 0; ++ clear_ipc.p.header_high.part.done = 1; ++ clear_ipc.p.header_low_payload = IPC_ACK_SUCCESS; ++ sst_shim_write64(sst_drv_ctx->shim, SST_IPCD, clear_ipc.full); ++ /* un mask busy interrupt */ ++ imr.part.busy_interrupt = 0; ++ sst_shim_write64(sst_drv_ctx->shim, SST_IMRX, imr.full); ++ spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); ++} ++ ++ ++/* ++ * process_fw_init - process the FW init msg ++ * ++ * @msg: IPC message mailbox data from FW ++ * ++ * This function processes the FW init msg from FW ++ * marks FW state and prints debug info of loaded FW ++ */ ++static void process_fw_init(void *msg) ++{ ++ struct ipc_header_fw_init *init = ++ (struct ipc_header_fw_init *)msg; ++ int retval = 0; ++ ++ pr_debug("*** FW Init msg came***\n"); ++ if (init->result) { ++ sst_drv_ctx->sst_state = SST_ERROR; ++ pr_debug("FW Init failed, Error %x\n", init->result); ++ pr_err("FW Init failed, Error %x\n", init->result); ++ retval = init->result; ++ goto ret; ++ } ++ pr_info("FW Version %02x.%02x.%02x.%02x\n", ++ init->fw_version.type, init->fw_version.major, ++ init->fw_version.minor, init->fw_version.build); ++ pr_info("Build date %s Time %s\n", ++ init->build_info.date, init->build_info.time); ++ ++ /* If there any runtime parameter to set, send it */ ++ if (sst_drv_ctx->runtime_param.param.addr) ++ sst_send_runtime_param(&(sst_drv_ctx->runtime_param.param)); ++ ++ret: ++ sst_wake_up_block(sst_drv_ctx, retval, FW_DWNL_ID, 0 , NULL, 0); ++} ++/** ++* sst_process_message_mfld - Processes message from SST ++* ++* @work: Pointer to work structure ++* ++* This function is scheduled by ISR ++* It take a msg from process_queue and does action based on msg ++*/ ++void sst_process_message_mfld(struct ipc_post *msg) ++{ ++ int str_id; ++ struct stream_info *stream; ++ ++ str_id = msg->header.part.str_id; ++ pr_debug("IPC process for %x\n", msg->header.full); ++ /* based on msg in list call respective handler */ ++ switch (msg->header.part.msg_id) { ++ case IPC_SST_PERIOD_ELAPSED: ++ if (sst_validate_strid(str_id)) { ++ pr_err("stream id %d invalid\n", str_id); ++ break; ++ } ++ stream = &sst_drv_ctx->streams[str_id]; ++ if (stream->period_elapsed) ++ stream->period_elapsed(stream->pcm_substream); ++ break; ++ case IPC_SST_BUF_UNDER_RUN: ++ case IPC_SST_BUF_OVER_RUN: ++ if (sst_validate_strid(str_id)) { ++ pr_err("stream id %d invalid\n", str_id); ++ break; ++ } ++ pr_err("Buffer under/overrun for %d\n", ++ msg->header.part.str_id); ++ pr_err("Got Underrun & not to send data...ignore\n"); ++ break; ++ ++ case IPC_SST_FRAGMENT_ELPASED: { ++ pr_debug("IPC_SST_FRAGMENT_ELPASED for %d", str_id); ++ sst_cdev_fragment_elapsed(str_id); ++ break; ++ } ++ ++ case IPC_IA_PRINT_STRING: ++ pr_debug("been asked to print something by fw\n"); ++ /* TBD */ ++ break; ++ ++ case IPC_IA_FW_INIT_CMPLT: { ++ /* send next data to FW */ ++ process_fw_init(msg->mailbox_data); ++ break; ++ } ++ ++ case IPC_SST_STREAM_PROCESS_FATAL_ERR: ++ if (sst_validate_strid(str_id)) { ++ pr_err("stream id %d invalid\n", str_id); ++ break; ++ } ++ pr_err("codec fatal error %x stream %d...\n", ++ msg->header.full, msg->header.part.str_id); ++ pr_err("Dropping the stream\n"); ++ sst_drop_stream(msg->header.part.str_id); ++ break; ++ default: ++ /* Illegal case */ ++ pr_err("Unhandled msg %x header %x\n", ++ msg->header.part.msg_id, msg->header.full); ++ } ++ return; ++} ++ ++/** ++* sst_process_message - Processes message from SST ++* ++* @work: Pointer to work structure ++* ++* This function is scheduled by ISR ++* It take a msg from process_queue and does action based on msg ++*/ ++ ++void sst_process_message_mrfld(struct ipc_post *msg) ++{ ++ int str_id; ++ ++ str_id = msg->mrfld_header.p.header_high.part.drv_id; ++ ++ pr_debug("IPC process message header %x payload %x\n", ++ msg->mrfld_header.p.header_high.full, ++ msg->mrfld_header.p.header_low_payload); ++ ++ return; ++} ++ ++#define VTSV_MAX_NUM_RESULTS 6 ++#define VTSV_SIZE_PER_RESULT 7 /* 7 16 bit words */ ++/* Max 6 results each of size 7 words + 1 num results word */ ++#define VTSV_MAX_TOTAL_RESULT_SIZE \ ++ (VTSV_MAX_NUM_RESULTS*VTSV_SIZE_PER_RESULT + 1) ++/* Each data word in the result is sent as a string in the format: ++DATAn=d, where n is the data word index varying from 0 to ++ VTSV_MAX_TOTAL_RESULT_SIZE-1 ++d = string representation of data in decimal format; ++ unsigned 16bit data needs max 5 chars ++So total data string size = 4("DATA")+2("n")+1("=") ++ +5("d")+1(null)+5(reserved) = 18 */ ++#define VTSV_DATA_STRING_SIZE 18 ++ ++static int send_vtsv_result_event(void *data, int size) ++{ ++ char *envp[VTSV_MAX_TOTAL_RESULT_SIZE+3]; ++ char res_size[30]; ++ char ev_type[30]; ++ char result[VTSV_MAX_TOTAL_RESULT_SIZE][VTSV_DATA_STRING_SIZE]; ++ int offset = 0; ++ u16 *tmp; ++ int i; ++ int ret; ++ ++ if (!data) { ++ pr_err("Data pointer Null into %s\n", __func__); ++ return -EINVAL; ++ } ++ size = size / (sizeof(u16)); /* Number of 16 bit data words*/ ++ if (size > VTSV_MAX_TOTAL_RESULT_SIZE) { ++ pr_err("VTSV result size exceeds expected value, no uevent sent\n"); ++ return -EINVAL; ++ } ++ ++ snprintf(ev_type, sizeof(res_size), "EVENT_TYPE=SST_VTSV"); ++ envp[offset++] = ev_type; ++ snprintf(res_size, sizeof(ev_type), "VTSV_RESULT_SIZE=%u", size); ++ envp[offset++] = res_size; ++ tmp = (u16 *)(data); ++ for (i = 0; i < size; i++) { ++ /* Driver assumes all data to be u16; The VTSV service ++ layer will type cast to u16 or s16 as appropriate for ++ a given data word*/ ++ snprintf(result[i], VTSV_DATA_STRING_SIZE, ++ "DATA%u=%u", i, *tmp++); ++ envp[offset++] = result[i]; ++ } ++ envp[offset] = NULL; ++ ret = kobject_uevent_env(&sst_drv_ctx->dev->kobj, KOBJ_CHANGE, envp); ++ if (ret) ++ pr_err("VTSV event send failed: ret = %d\n", ret); ++ return ret; ++} ++ ++static void process_fw_async_msg(struct ipc_post *msg) ++{ ++ u32 msg_id; ++ int str_id; ++ int res_size, ret; ++ u32 data_size, i; ++ void *data_offset; ++ struct stream_info *stream; ++ union ipc_header_high msg_high; ++ u32 msg_low, pipe_id; ++ ++ msg_high = msg->mrfld_header.p.header_high; ++ msg_low = msg->mrfld_header.p.header_low_payload; ++ msg_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->cmd_id; ++ data_offset = (msg->mailbox_data + sizeof(struct ipc_dsp_hdr)); ++ data_size = msg_low - (sizeof(struct ipc_dsp_hdr)); ++ ++ switch (msg_id) { ++ case IPC_SST_PERIOD_ELAPSED_MRFLD: ++ pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id; ++ str_id = get_stream_id_mrfld(pipe_id); ++ if (str_id > 0) { ++ pr_debug("Period elapsed rcvd for pipe id 0x%x\n", pipe_id); ++ stream = &sst_drv_ctx->streams[str_id]; ++ if (stream->period_elapsed) ++ stream->period_elapsed(stream->pcm_substream); ++ if (stream->compr_cb) ++ stream->compr_cb(stream->compr_cb_param); ++ } ++ break; ++ ++ case IPC_IA_DRAIN_STREAM_MRFLD: ++ pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id; ++ str_id = get_stream_id_mrfld(pipe_id); ++ if (str_id > 0) { ++ stream = &sst_drv_ctx->streams[str_id]; ++ if (stream->drain_notify) ++ stream->drain_notify(stream->drain_cb_param); ++ } ++ break; ++ ++ case IPC_IA_FW_ASYNC_ERR_MRFLD: ++ pr_err("FW sent async error msg:\n"); ++ for (i = 0; i < (data_size/4); i++) ++ pr_err("0x%x\n", (*((unsigned int *)data_offset + i))); ++ break; ++ ++ case IPC_IA_VTSV_DETECTED: ++ res_size = data_size; ++ ret = send_vtsv_result_event(data_offset, res_size); ++ if (ret) ++ pr_err("VTSV uevent send failed: %d\n", ret); ++ else ++ pr_debug("VTSV uevent sent\n"); ++ break; ++ ++ case IPC_IA_FW_INIT_CMPLT_MRFLD: ++ process_fw_init(data_offset); ++ break; ++ ++ case IPC_IA_BUF_UNDER_RUN_MRFLD: ++ pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id; ++ str_id = get_stream_id_mrfld(pipe_id); ++ if (str_id > 0) ++ pr_err("Buffer under-run for pipe:%#x str_id:%d\n", ++ pipe_id, str_id); ++ break; ++ ++ default: ++ pr_err("Unrecognized async msg from FW msg_id %#x\n", msg_id); ++ } ++} ++ ++void sst_process_reply_mrfld(struct ipc_post *msg) ++{ ++ unsigned int drv_id; ++ void *data; ++ union ipc_header_high msg_high; ++ u32 msg_low; ++ ++ msg_high = msg->mrfld_header.p.header_high; ++ msg_low = msg->mrfld_header.p.header_low_payload; ++ ++ pr_debug("IPC process message header %x payload %x\n", ++ msg->mrfld_header.p.header_high.full, ++ msg->mrfld_header.p.header_low_payload); ++ ++ drv_id = msg_high.part.drv_id; ++ ++ /* Check for async messages */ ++ if (drv_id == SST_ASYNC_DRV_ID) { ++ /* FW sent async large message */ ++ process_fw_async_msg(msg); ++ goto end; ++ } ++ ++ /* FW sent short error response for an IPC */ ++ if (msg_high.part.result && drv_id && !msg_high.part.large) { ++ /* 32-bit FW error code in msg_low */ ++ pr_err("FW sent error response 0x%x", msg_low); ++ sst_wake_up_block(sst_drv_ctx, msg_high.part.result, ++ msg_high.part.drv_id, ++ msg_high.part.msg_id, NULL, 0); ++ goto end; ++ } ++ ++ /* Process all valid responses */ ++ /* if it is a large message, the payload contains the size to ++ * copy from mailbox */ ++ if (msg_high.part.large) { ++ data = kzalloc(msg_low, GFP_KERNEL); ++ if (!data) ++ goto end; ++ memcpy(data, (void *) msg->mailbox_data, msg_low); ++ if (sst_wake_up_block(sst_drv_ctx, msg_high.part.result, ++ msg_high.part.drv_id, ++ msg_high.part.msg_id, data, msg_low)) ++ kfree(data); ++ } else { ++ sst_wake_up_block(sst_drv_ctx, msg_high.part.result, ++ msg_high.part.drv_id, ++ msg_high.part.msg_id, NULL, 0); ++ } ++ ++end: ++ return; ++} ++ ++/** ++* sst_process_reply - Processes reply message from SST ++* ++* @work: Pointer to work structure ++* ++* This function is scheduled by ISR ++* It take a reply msg from response_queue and ++* does action based on msg ++*/ ++void sst_process_reply_mfld(struct ipc_post *msg) ++{ ++ void *data; ++ int str_id; ++ struct stream_info *stream; ++ ++ ++ str_id = msg->header.part.str_id; ++ ++ pr_debug("sst: IPC process reply for %x\n", msg->header.full); ++ ++ /* handle drain notify first */ ++ if (msg->header.part.msg_id == IPC_IA_DRAIN_STREAM) { ++ pr_debug("drain message notify\n"); ++ if (str_id > 0) { ++ stream = &sst_drv_ctx->streams[str_id]; ++ if (stream->drain_notify) ++ stream->drain_notify(stream->drain_cb_param); ++ } ++ return; ++ } ++ ++ ++ if (!msg->header.part.large) { ++ if (!msg->header.part.data) ++ pr_debug("Success\n"); ++ else ++ pr_err("Error from firmware: %d\n", msg->header.part.data); ++ sst_wake_up_block(sst_drv_ctx, msg->header.part.data, ++ str_id, msg->header.part.msg_id, NULL, 0); ++ } else { ++ pr_debug("Allocating %d\n", msg->header.part.data); ++ data = kzalloc(msg->header.part.data, GFP_KERNEL); ++ if (!data) { ++ pr_err("sst: mem alloc failed\n"); ++ return; ++ } ++ ++ memcpy(data, (void *)msg->mailbox_data, msg->header.part.data); ++ if (sst_wake_up_block(sst_drv_ctx, 0, str_id, ++ msg->header.part.msg_id, data, ++ msg->header.part.data)) ++ kfree(data); ++ } ++ return; ++} +diff --git a/sound/soc/intel/sst/sst_pvt.c b/sound/soc/intel/sst/sst_pvt.c +new file mode 100644 +index 0000000..7638cb8 +--- /dev/null ++++ b/sound/soc/intel/sst/sst_pvt.c +@@ -0,0 +1,577 @@ ++/* ++ * sst_pvt.c - Intel SST Driver for audio engine ++ * ++ * Copyright (C) 2008-10 Intel Corp ++ * Authors: Vinod Koul ++ * Harsha Priya ++ * Dharageswari R ++ * KP Jeeja ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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. ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * This file contains all private functions ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "../sst_platform.h" ++#include "../platform_ipc_v2.h" ++#include "sst.h" ++ ++#define SST_EXCE_DUMP_BASE 0xFFFF2c00 ++#define SST_EXCE_DUMP_WORD 4 ++#define SST_EXCE_DUMP_LEN 32 ++#define SST_EXCE_DUMP_SIZE ((SST_EXCE_DUMP_LEN)*(SST_EXCE_DUMP_WORD)) ++#define SST_EXCE_DUMP_OFFSET 0xA00 ++/* ++ * sst_wait_interruptible - wait on event ++ * ++ * @sst_drv_ctx: Driver context ++ * @block: Driver block to wait on ++ * ++ * This function waits without a timeout (and is interruptable) for a ++ * given block event ++ */ ++int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx, ++ struct sst_block *block) ++{ ++ int retval = 0; ++ ++ if (!wait_event_interruptible(sst_drv_ctx->wait_queue, ++ block->condition)) { ++ /* event wake */ ++ if (block->ret_code < 0) { ++ pr_err("stream failed %d\n", block->ret_code); ++ retval = -EBUSY; ++ } else { ++ pr_debug("event up\n"); ++ retval = 0; ++ } ++ } else { ++ pr_err("signal interrupted\n"); ++ retval = -EINTR; ++ } ++ return retval; ++ ++} ++ ++unsigned long long read_shim_data(struct intel_sst_drv *sst, int addr) ++{ ++ unsigned long long val = 0; ++ ++ switch (sst->pci_id) { ++ case SST_CLV_PCI_ID: ++ val = sst_shim_read(sst->shim, addr); ++ break; ++ case SST_MRFLD_PCI_ID: ++ case SST_BYT_PCI_ID: ++ val = sst_shim_read64(sst->shim, addr); ++ break; ++ } ++ return val; ++} ++ ++void write_shim_data(struct intel_sst_drv *sst, int addr, ++ unsigned long long data) ++{ ++ switch (sst->pci_id) { ++ case SST_CLV_PCI_ID: ++ sst_shim_write(sst->shim, addr, (u32) data); ++ break; ++ case SST_MRFLD_PCI_ID: ++ case SST_BYT_PCI_ID: ++ sst_shim_write64(sst->shim, addr, (u64) data); ++ break; ++ } ++} ++ ++ ++void dump_sst_shim(struct intel_sst_drv *sst) ++{ ++ unsigned long irq_flags; ++ ++ spin_lock_irqsave(&sst->ipc_spin_lock, irq_flags); ++ pr_err("audio shim registers:\n" ++ "CSR: %.8llx\n" ++ "PISR: %.8llx\n" ++ "PIMR: %.8llx\n" ++ "ISRX: %.8llx\n" ++ "ISRD: %.8llx\n" ++ "IMRX: %.8llx\n" ++ "IMRD: %.8llx\n" ++ "IPCX: %.8llx\n" ++ "IPCD: %.8llx\n" ++ "ISRSC: %.8llx\n" ++ "ISRLPESC: %.8llx\n" ++ "IMRSC: %.8llx\n" ++ "IMRLPESC: %.8llx\n" ++ "IPCSC: %.8llx\n" ++ "IPCLPESC: %.8llx\n" ++ "CLKCTL: %.8llx\n" ++ "CSR2: %.8llx\n", ++ read_shim_data(sst, SST_CSR), ++ read_shim_data(sst, SST_PISR), ++ read_shim_data(sst, SST_PIMR), ++ read_shim_data(sst, SST_ISRX), ++ read_shim_data(sst, SST_ISRD), ++ read_shim_data(sst, SST_IMRX), ++ read_shim_data(sst, SST_IMRD), ++ read_shim_data(sst, sst->ipc_reg.ipcx), ++ read_shim_data(sst, sst->ipc_reg.ipcd), ++ read_shim_data(sst, SST_ISRSC), ++ read_shim_data(sst, SST_ISRLPESC), ++ read_shim_data(sst, SST_IMRSC), ++ read_shim_data(sst, SST_IMRLPESC), ++ read_shim_data(sst, SST_IPCSC), ++ read_shim_data(sst, SST_IPCLPESC), ++ read_shim_data(sst, SST_CLKCTL), ++ read_shim_data(sst, SST_CSR2)); ++ spin_unlock_irqrestore(&sst->ipc_spin_lock, irq_flags); ++} ++ ++void reset_sst_shim(struct intel_sst_drv *sst) ++{ ++ union config_status_reg_mrfld csr; ++ ++ pr_err("Resetting few Shim registers\n"); ++ write_shim_data(sst, sst->ipc_reg.ipcx, 0x0); ++ write_shim_data(sst, sst->ipc_reg.ipcd, 0x0); ++ write_shim_data(sst, SST_ISRX, 0x0); ++ write_shim_data(sst, SST_ISRD, 0x0); ++ write_shim_data(sst, SST_IPCSC, 0x0); ++ write_shim_data(sst, SST_IPCLPESC, 0x0); ++ write_shim_data(sst, SST_ISRSC, 0x0); ++ write_shim_data(sst, SST_ISRLPESC, 0x0); ++ write_shim_data(sst, SST_PISR, 0x0); ++ ++ /* Reset the CSR value to the default value. i.e 0x1e40001*/ ++ csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR); ++ csr.part.xt_snoop = 0; ++ csr.full &= ~(0xf); ++ csr.full |= 0x01; ++ sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full); ++} ++ ++static void dump_sst_crash_area(void) ++{ ++ void __iomem *fw_dump_area; ++ u32 dump_word; ++ u8 i; ++ ++ /* dump the firmware SRAM where the exception details are stored */ ++ fw_dump_area = ioremap_nocache(SST_EXCE_DUMP_BASE, SST_EXCE_DUMP_SIZE); ++ ++ pr_err("Firmware exception dump begins:\n"); ++ pr_err("Exception start signature:%#x\n", readl(fw_dump_area + SST_EXCE_DUMP_WORD)); ++ pr_err("EXCCAUSE:\t\t\t%#x\n", readl(fw_dump_area + SST_EXCE_DUMP_WORD*2)); ++ pr_err("EXCVADDR:\t\t\t%#x\n", readl(fw_dump_area + (SST_EXCE_DUMP_WORD*3))); ++ pr_err("Firmware additional data:\n"); ++ ++ /* dump remaining FW debug data */ ++ for (i = 1; i < (SST_EXCE_DUMP_LEN-4+1); i++) { ++ dump_word = readl(fw_dump_area + (SST_EXCE_DUMP_WORD*3) ++ + (i*SST_EXCE_DUMP_WORD)); ++ pr_err("Data[%d]=%#x\n", i, dump_word); ++ } ++ iounmap(fw_dump_area); ++ pr_err("Firmware exception dump ends\n"); ++} ++ ++/** ++ * dump_ram_area - dumps the iram/dram into a local buff ++ * ++ * @sst : pointer to driver context ++ * @recovery : pointer to the struct containing buffers ++ * @iram : true if iram dump else false ++ * This function dumps the iram dram data into the respective buffers ++ */ ++static void dump_ram_area(struct intel_sst_drv *sst, ++ struct sst_dump_buf *dump_buf, enum sst_ram_type type) ++{ ++ if (type == SST_IRAM) { ++ pr_err("Iram dumped in buffer\n"); ++ memcpy_fromio(dump_buf->iram_buf.buf, sst->iram, ++ dump_buf->iram_buf.size); ++ } else { ++ pr_err("Dram dumped in buffer\n"); ++ memcpy_fromio(dump_buf->dram_buf.buf, sst->dram, ++ dump_buf->dram_buf.size); ++ } ++} ++ ++/*FIXME Disabling IRAM/DRAM dump for timeout issues */ ++static void sst_stream_recovery(struct intel_sst_drv *sst) ++{ ++ struct stream_info *str_info; ++ u8 i; ++ for (i = 1; i <= sst->info.max_streams; i++) { ++ pr_err("Audio: Stream %d, state %d\n", i, sst->streams[i].status); ++ if (sst->streams[i].status != STREAM_UN_INIT) { ++ str_info = &sst_drv_ctx->streams[i]; ++ if (str_info->pcm_substream) ++ snd_pcm_stop(str_info->pcm_substream, SNDRV_PCM_STATE_SETUP); ++ else if (str_info->compr_cb_param) ++ snd_compr_stop(str_info->compr_cb_param); ++ sst->streams[i].status = STREAM_RESET; ++ } ++ } ++} ++ ++static void sst_dump_ipc_dispatch_lists(struct intel_sst_drv *sst) ++{ ++ struct ipc_post *m, *_m; ++ unsigned long irq_flags; ++ ++ spin_lock_irqsave(&sst->ipc_spin_lock, irq_flags); ++ if (list_empty(&sst->ipc_dispatch_list)) ++ pr_err("ipc dispatch list is Empty\n"); ++ ++ list_for_each_entry_safe(m, _m, &sst->ipc_dispatch_list, node) { ++ pr_err("ipc-dispatch:pending msg header %#x\n", m->header.full); ++ list_del(&m->node); ++ kfree(m->mailbox_data); ++ kfree(m); ++ } ++ spin_unlock_irqrestore(&sst->ipc_spin_lock, irq_flags); ++} ++ ++static void sst_dump_rx_lists(struct intel_sst_drv *sst) ++{ ++ struct ipc_post *m, *_m; ++ unsigned long irq_flags; ++ ++ spin_lock_irqsave(&sst->rx_msg_lock, irq_flags); ++ if (list_empty(&sst->rx_list)) ++ pr_err("rx msg list is empty\n"); ++ ++ list_for_each_entry_safe(m, _m, &sst->rx_list, node) { ++ pr_err("rx: pending msg header %#x\n", m->header.full); ++ list_del(&m->node); ++ kfree(m->mailbox_data); ++ kfree(m); ++ } ++ spin_unlock_irqrestore(&sst->rx_msg_lock, irq_flags); ++} ++ ++/* num_dwords: should be multiple of 4 */ ++static void dump_buffer_fromio(void __iomem *from, ++ unsigned int num_dwords) ++{ ++ int i; ++ u32 val[4]; ++ ++ if (num_dwords % 4) { ++ pr_err("%s: num_dwords %d not multiple of 4\n", ++ __func__, num_dwords); ++ return; ++ } ++ ++ pr_err("****** Start *******\n"); ++ pr_err("Dump %d dwords, from location %p\n", num_dwords, from); ++ ++ for (i = 0; i < num_dwords; ) { ++ val[0] = ioread32(from + (i++ * 4)); ++ val[1] = ioread32(from + (i++ * 4)); ++ val[2] = ioread32(from + (i++ * 4)); ++ val[3] = ioread32(from + (i++ * 4)); ++ pr_err("%.8x %.8x %.8x %.8x\n", val[0], val[1], val[2], val[3]); ++ } ++ pr_err("****** End *********\n\n\n"); ++} ++ ++static void sst_stall_lpe_n_wait(struct intel_sst_drv *sst) ++{ ++ union config_status_reg_mrfld csr; ++ void __iomem *dma_reg0 = sst->debugfs.dma_reg[0]; ++ void __iomem *dma_reg1 = sst->debugfs.dma_reg[1]; ++ int offset = 0x3A0; /* ChEnReg of DMA */ ++ ++ ++ pr_err("Before stall: DMA_0 Ch_EN %#llx DMA_1 Ch_EN %#llx\n", ++ sst_reg_read64(dma_reg0, offset), ++ sst_reg_read64(dma_reg1, offset)); ++ ++ /* Stall LPE */ ++ csr.full = sst_shim_read64(sst->shim, SST_CSR); ++ csr.part.runstall = 1; ++ sst_shim_write64(sst->shim, SST_CSR, csr.full); ++ ++ /* A 5ms delay, before resetting the LPE */ ++ usleep_range(5000, 5100); ++ ++ pr_err("After stall: DMA_0 Ch_EN %#llx DMA_1 Ch_EN %#llx\n", ++ sst_reg_read64(dma_reg0, offset), ++ sst_reg_read64(dma_reg1, offset)); ++} ++ ++#if IS_ENABLED(CONFIG_INTEL_SCU_IPC) ++static void sst_send_scu_reset_ipc(struct intel_sst_drv *sst) ++{ ++ int ret = 0; ++ ++ /* Reset and power gate the LPE */ ++ ret = intel_scu_ipc_simple_command(IPC_SCU_LPE_RESET, 0); ++ if (ret) { ++ pr_err("Power gating LPE failed %d\n", ret); ++ reset_sst_shim(sst); ++ } else { ++ pr_err("LPE reset via SCU is success!!\n"); ++ pr_err("dump after LPE power cycle\n"); ++ dump_sst_shim(sst); ++ ++ /* Mask the DMA & SSP interrupts */ ++ sst_shim_write64(sst->shim, SST_IMRX, 0xFFFF0038); ++ } ++} ++#else ++static void sst_send_scu_reset_ipc(struct intel_sst_drv *sst) ++{ ++ pr_debug("%s: do nothing, just return\n", __func__); ++} ++#endif ++ ++#define SRAM_OFFSET_MRFLD 0xc00 ++#define NUM_DWORDS 256 ++void sst_do_recovery_mrfld(struct intel_sst_drv *sst) ++{ ++ char iram_event[30], dram_event[30], ddr_imr_event[65], event_type[30]; ++ char *envp[5]; ++ int env_offset = 0; ++ ++ /* ++ * setting firmware state as uninit so that the firmware will get ++ * redownloaded on next request.This is because firmare not responding ++ * for 1 sec is equalant to some unrecoverable error of FW. ++ */ ++ pr_err("Audio: Intel SST engine encountered an unrecoverable error\n"); ++ pr_err("Audio: trying to reset the dsp now\n"); ++ ++ mutex_lock(&sst->sst_lock); ++ sst->sst_state = SST_UN_INIT; ++ sst_stream_recovery(sst); ++ mutex_unlock(&sst->sst_lock); ++ ++ dump_stack(); ++ dump_sst_shim(sst); ++ ++ sst_stall_lpe_n_wait(sst); ++ ++ /* dump mailbox and sram */ ++ pr_err("Dumping Mailbox...\n"); ++ dump_buffer_fromio(sst->mailbox, NUM_DWORDS); ++ pr_err("Dumping SRAM...\n"); ++ dump_buffer_fromio(sst->mailbox + SRAM_OFFSET_MRFLD, NUM_DWORDS); ++ ++ if (sst_drv_ctx->ops->set_bypass) { ++ ++ sst_drv_ctx->ops->set_bypass(true); ++ dump_ram_area(sst, &(sst->dump_buf), SST_IRAM); ++ dump_ram_area(sst, &(sst->dump_buf), SST_DRAM); ++ sst_drv_ctx->ops->set_bypass(false); ++ ++ } ++ ++ snprintf(event_type, sizeof(event_type), "EVENT_TYPE=SST_RECOVERY"); ++ envp[env_offset++] = event_type; ++ snprintf(iram_event, sizeof(iram_event), "IRAM_DUMP_SIZE=%d", ++ sst->dump_buf.iram_buf.size); ++ envp[env_offset++] = iram_event; ++ snprintf(dram_event, sizeof(dram_event), "DRAM_DUMP_SIZE=%d", ++ sst->dump_buf.dram_buf.size); ++ envp[env_offset++] = dram_event; ++ ++ if (sst->ddr != NULL) { ++ snprintf(ddr_imr_event, sizeof(ddr_imr_event), ++ "DDR_IMR_DUMP_SIZE=%d DDR_IMR_ADDRESS=%p", (sst->ddr_end - sst->ddr_base), sst->ddr); ++ envp[env_offset++] = ddr_imr_event; ++ } ++ envp[env_offset] = NULL; ++ kobject_uevent_env(&sst->dev->kobj, KOBJ_CHANGE, envp); ++ pr_err("Recovery Uevent Sent!!\n"); ++ ++ /* Send IPC to SCU to power gate and reset the LPE */ ++ sst_send_scu_reset_ipc(sst); ++ ++ pr_err("reset the pvt id from val %d\n", sst_drv_ctx->pvt_id); ++ spin_lock(&sst_drv_ctx->pvt_id_lock); ++ sst_drv_ctx->pvt_id = 0; ++ spin_unlock(&sst_drv_ctx->pvt_id_lock); ++ sst_dump_ipc_dispatch_lists(sst_drv_ctx); ++ sst_dump_rx_lists(sst_drv_ctx); ++ ++ if (sst_drv_ctx->fw_in_mem) { ++ pr_err("Clearing the cached FW copy...\n"); ++ kfree(sst_drv_ctx->fw_in_mem); ++ sst_drv_ctx->fw_in_mem = NULL; ++ } ++} ++ ++void sst_do_recovery(struct intel_sst_drv *sst) ++{ ++ pr_err("Audio: Intel SST engine encountered an unrecoverable error\n"); ++ ++ dump_stack(); ++ dump_sst_shim(sst); ++ ++ if (sst->sst_state == SST_FW_RUNNING && ++ sst_drv_ctx->pci_id == SST_CLV_PCI_ID) ++ dump_sst_crash_area(); ++ ++ sst_dump_ipc_dispatch_lists(sst_drv_ctx); ++ ++} ++ ++/* ++ * sst_wait_timeout - wait on event for timeout ++ * ++ * @sst_drv_ctx: Driver context ++ * @block: Driver block to wait on ++ * ++ * This function waits with a timeout value (and is not interruptible) on a ++ * given block event ++ */ ++int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx, struct sst_block *block) ++{ ++ int retval = 0; ++ ++ /* NOTE: ++ Observed that FW processes the alloc msg and replies even ++ before the alloc thread has finished execution */ ++ pr_debug("sst: waiting for condition %x ipc %d drv_id %d\n", ++ block->condition, block->msg_id, block->drv_id); ++ if (wait_event_timeout(sst_drv_ctx->wait_queue, ++ block->condition, ++ msecs_to_jiffies(SST_BLOCK_TIMEOUT))) { ++ /* event wake */ ++ pr_debug("sst: Event wake %x\n", block->condition); ++ pr_debug("sst: message ret: %d\n", block->ret_code); ++ retval = -block->ret_code; ++ } else { ++ block->on = false; ++ pr_err("sst: Wait timed-out condition:%#x, msg_id:%#x fw_state %#x\n", ++ block->condition, block->msg_id, sst_drv_ctx->sst_state); ++ ++ if (sst_drv_ctx->sst_state == SST_FW_LOADED || ++ sst_drv_ctx->sst_state == SST_START_INIT) { ++ pr_err("Can't recover as timedout while downloading the FW\n"); ++ pr_err("reseting fw state to unint from %d ...\n", sst_drv_ctx->sst_state); ++ sst_drv_ctx->sst_state = SST_UN_INIT; ++ ++ dump_sst_shim(sst_drv_ctx); ++ ++ /* Reset & Power Off the LPE only for MRFLD */ ++ if (sst_drv_ctx->pci_id == SST_MRFLD_PCI_ID) { ++ sst_stall_lpe_n_wait(sst_drv_ctx); ++ ++ /* Send IPC to SCU to power gate and reset the LPE */ ++ sst_send_scu_reset_ipc(sst_drv_ctx); ++ } ++ ++ } else { ++ if (sst_drv_ctx->ops->do_recovery) ++ sst_drv_ctx->ops->do_recovery(sst_drv_ctx); ++ } ++ ++ retval = -EBUSY; ++ } ++ return retval; ++} ++ ++/* ++ * sst_create_ipc_msg - create a IPC message ++ * ++ * @arg: ipc message ++ * @large: large or short message ++ * ++ * this function allocates structures to send a large or short ++ * message to the firmware ++ */ ++int sst_create_ipc_msg(struct ipc_post **arg, bool large) ++{ ++ struct ipc_post *msg; ++ ++ msg = kzalloc(sizeof(struct ipc_post), GFP_ATOMIC); ++ if (!msg) { ++ pr_err("kzalloc ipc msg failed\n"); ++ return -ENOMEM; ++ } ++ if (large) { ++ msg->mailbox_data = kzalloc(SST_MAILBOX_SIZE, GFP_ATOMIC); ++ if (!msg->mailbox_data) { ++ kfree(msg); ++ pr_err("kzalloc mailbox_data failed"); ++ return -ENOMEM; ++ } ++ } else { ++ msg->mailbox_data = NULL; ++ } ++ msg->is_large = large; ++ *arg = msg; ++ return 0; ++} ++ ++/* ++ * sst_create_block_and_ipc_msg - Creates IPC message and sst block ++ * @arg: passed to sst_create_ipc_message API ++ * @large: large or short message ++ * @sst_drv_ctx: sst driver context ++ * @block: return block allocated ++ * @msg_id: IPC ++ * @drv_id: stream id or private id ++ */ ++int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large, ++ struct intel_sst_drv *sst_drv_ctx, struct sst_block **block, ++ u32 msg_id, u32 drv_id) ++{ ++ int retval = 0; ++ retval = sst_create_ipc_msg(arg, large); ++ if (retval) ++ return retval; ++ *block = sst_create_block(sst_drv_ctx, msg_id, drv_id); ++ if (*block == NULL) { ++ kfree(*arg); ++ return -ENOMEM; ++ } ++ return retval; ++} ++ ++/* ++ * sst_clean_stream - clean the stream context ++ * ++ * @stream: stream structure ++ * ++ * this function resets the stream contexts ++ * should be called in free ++ */ ++void sst_clean_stream(struct stream_info *stream) ++{ ++ stream->status = STREAM_UN_INIT; ++ stream->prev = STREAM_UN_INIT; ++ mutex_lock(&stream->lock); ++ stream->cumm_bytes = 0; ++ mutex_unlock(&stream->lock); ++} ++ +diff --git a/sound/soc/intel/sst/sst_stream.c b/sound/soc/intel/sst/sst_stream.c +new file mode 100644 +index 0000000..9e71b44 +--- /dev/null ++++ b/sound/soc/intel/sst/sst_stream.c +@@ -0,0 +1,831 @@ ++/* ++ * sst_stream.c - Intel SST Driver for audio engine ++ * ++ * Copyright (C) 2008-10 Intel Corp ++ * Authors: Vinod Koul ++ * Harsha Priya ++ * Dharageswari R ++ * KP Jeeja ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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. ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * This file contains the stream operations of SST driver ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "../sst_platform.h" ++#include "../platform_ipc_v2.h" ++#include "sst.h" ++#include "sst_trace.h" ++ ++/** ++ * sst_alloc_stream - Send msg for a new stream ID ++ * ++ * @params: stream params ++ * @stream_ops: operation of stream PB/capture ++ * @codec: codec for stream ++ * @device: device stream to be allocated for ++ * ++ * This function is called by any function which wants to start ++ * a new stream. This also check if a stream exists which is idle ++ * it initializes idle stream id to this request ++ */ ++int sst_alloc_stream_ctp(char *params, struct sst_block *block) ++{ ++ struct ipc_post *msg = NULL; ++ struct snd_sst_alloc_params alloc_param; ++ unsigned int pcm_slot = 0x03, num_ch; ++ int str_id; ++ struct snd_sst_params *str_params; ++ struct snd_sst_stream_params *sparams; ++ struct snd_sst_alloc_params_ext *aparams; ++ struct stream_info *str_info; ++ unsigned int stream_ops, device; ++ u8 codec; ++ ++ pr_debug("In %s\n", __func__); ++ ++ BUG_ON(!params); ++ str_params = (struct snd_sst_params *)params; ++ stream_ops = str_params->ops; ++ codec = str_params->codec; ++ device = str_params->device_type; ++ sparams = &str_params->sparams; ++ aparams = &str_params->aparams; ++ num_ch = sst_get_num_channel(str_params); ++ ++ pr_debug("period_size = %d\n", aparams->frag_size); ++ pr_debug("ring_buf_addr = 0x%x\n", aparams->ring_buf_info[0].addr); ++ pr_debug("ring_buf_size = %d\n", aparams->ring_buf_info[0].size); ++ pr_debug("In alloc device_type=%d\n", str_params->device_type); ++ pr_debug("In alloc sg_count =%d\n", aparams->sg_count); ++ ++ str_id = str_params->stream_id; ++ if (str_id <= 0) ++ return -EBUSY; ++ ++ /*allocate device type context*/ ++ sst_init_stream(&sst_drv_ctx->streams[str_id], codec, ++ str_id, stream_ops, pcm_slot); ++ /* send msg to FW to allocate a stream */ ++ if (sst_create_ipc_msg(&msg, true)) ++ return -ENOMEM; ++ ++ alloc_param.str_type.codec_type = codec; ++ alloc_param.str_type.str_type = str_params->stream_type; ++ alloc_param.str_type.operation = stream_ops; ++ alloc_param.str_type.protected_str = 0; /* non drm */ ++ alloc_param.str_type.time_slots = pcm_slot; ++ alloc_param.str_type.reserved = 0; ++ alloc_param.str_type.result = 0; ++ memcpy(&alloc_param.stream_params, sparams, ++ sizeof(struct snd_sst_stream_params)); ++ memcpy(&alloc_param.alloc_params, aparams, ++ sizeof(struct snd_sst_alloc_params_ext)); ++ block->drv_id = str_id; ++ block->msg_id = IPC_IA_ALLOC_STREAM; ++ sst_fill_header(&msg->header, IPC_IA_ALLOC_STREAM, 1, str_id); ++ msg->header.part.data = sizeof(alloc_param) + sizeof(u32); ++ memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); ++ memcpy(msg->mailbox_data + sizeof(u32), &alloc_param, ++ sizeof(alloc_param)); ++ str_info = &sst_drv_ctx->streams[str_id]; ++ str_info->num_ch = num_ch; ++ sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); ++ return str_id; ++} ++ ++int sst_alloc_stream_mrfld(char *params, struct sst_block *block) ++{ ++ struct ipc_post *msg = NULL; ++ struct snd_sst_alloc_mrfld alloc_param; ++ struct ipc_dsp_hdr dsp_hdr; ++ struct snd_sst_params *str_params; ++ struct snd_sst_tstamp fw_tstamp; ++ unsigned int str_id, pipe_id, pvt_id, task_id; ++ u32 len = 0; ++ struct stream_info *str_info; ++ int i, num_ch; ++ ++ pr_debug("In %s\n", __func__); ++ BUG_ON(!params); ++ ++ str_params = (struct snd_sst_params *)params; ++ memset(&alloc_param, 0, sizeof(alloc_param)); ++ alloc_param.operation = str_params->ops; ++ alloc_param.codec_type = str_params->codec; ++ alloc_param.sg_count = str_params->aparams.sg_count; ++ alloc_param.ring_buf_info[0].addr = str_params->aparams.ring_buf_info[0].addr; ++ alloc_param.ring_buf_info[0].size = str_params->aparams.ring_buf_info[0].size; ++ alloc_param.frag_size = str_params->aparams.frag_size; ++ ++ memcpy(&alloc_param.codec_params, &str_params->sparams, ++ sizeof(struct snd_sst_stream_params)); ++ ++ /* fill channel map params for multichannel support. ++ * Ideally channel map should be received from upper layers ++ * for multichannel support. ++ * Currently hardcoding as per FW reqm. ++ */ ++ num_ch = sst_get_num_channel(str_params); ++ for (i = 0; i < 8; i++) { ++ if (i < num_ch) ++ alloc_param.codec_params.uc.pcm_params.channel_map[i] = i; ++ else ++ alloc_param.codec_params.uc.pcm_params.channel_map[i] = 0xFF; ++ } ++ ++ str_id = str_params->stream_id; ++ pipe_id = str_params->device_type; ++ task_id = str_params->task; ++ sst_drv_ctx->streams[str_id].pipe_id = pipe_id; ++ sst_drv_ctx->streams[str_id].task_id = task_id; ++ sst_drv_ctx->streams[str_id].num_ch = num_ch; ++ ++ pvt_id = sst_assign_pvt_id(sst_drv_ctx); ++ if (sst_drv_ctx->info.lpe_viewpt_rqd) ++ alloc_param.ts = sst_drv_ctx->info.mailbox_start + ++ sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp)); ++ else ++ alloc_param.ts = sst_drv_ctx->mailbox_add + ++ sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp)); ++ ++ pr_debug("alloc tstamp location = 0x%x\n", alloc_param.ts); ++ pr_debug("assigned pipe id 0x%x to task %d\n", pipe_id, task_id); ++ ++ /*allocate device type context*/ ++ sst_init_stream(&sst_drv_ctx->streams[str_id], alloc_param.codec_type, ++ str_id, alloc_param.operation, 0); ++ /* send msg to FW to allocate a stream */ ++ if (sst_create_ipc_msg(&msg, true)) ++ return -ENOMEM; ++ ++ block->drv_id = pvt_id; ++ block->msg_id = IPC_CMD; ++ ++ sst_fill_header_mrfld(&msg->mrfld_header, IPC_CMD, ++ task_id, 1, pvt_id); ++ pr_debug("header:%x\n", msg->mrfld_header.p.header_high.full); ++ msg->mrfld_header.p.header_high.part.res_rqd = 1; ++ ++ len = msg->mrfld_header.p.header_low_payload = sizeof(alloc_param) + sizeof(dsp_hdr); ++ sst_fill_header_dsp(&dsp_hdr, IPC_IA_ALLOC_STREAM_MRFLD, pipe_id, sizeof(alloc_param)); ++ memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr)); ++ memcpy(msg->mailbox_data + sizeof(dsp_hdr), &alloc_param, ++ sizeof(alloc_param)); ++ trace_sst_stream("ALLOC ->", str_id, pipe_id); ++ str_info = &sst_drv_ctx->streams[str_id]; ++ pr_debug("header:%x\n", msg->mrfld_header.p.header_high.full); ++ pr_debug("response rqd: %x", msg->mrfld_header.p.header_high.part.res_rqd); ++ pr_debug("calling post_message\n"); ++ pr_info("Alloc for str %d pipe %#x\n", str_id, pipe_id); ++ ++ sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); ++ return str_id; ++} ++ ++/** ++* sst_stream_stream - Send msg for a pausing stream ++* @str_id: stream ID ++* ++* This function is called by any function which wants to start ++* a stream. ++*/ ++int sst_start_stream(int str_id) ++{ ++ int retval = 0, pvt_id; ++ u32 len = 0; ++ struct ipc_post *msg = NULL; ++ struct ipc_dsp_hdr dsp_hdr; ++ struct stream_info *str_info; ++ ++ pr_debug("sst_start_stream for %d\n", str_id); ++ str_info = get_stream_info(str_id); ++ if (!str_info) ++ return -EINVAL; ++ if (str_info->status != STREAM_RUNNING) ++ return -EBADRQC; ++ ++ if (sst_create_ipc_msg(&msg, true)) ++ return -ENOMEM; ++ ++ if (!sst_drv_ctx->use_32bit_ops) { ++ pvt_id = sst_assign_pvt_id(sst_drv_ctx); ++ pr_debug("pvt_id = %d, pipe id = %d, task = %d\n", ++ pvt_id, str_info->pipe_id, str_info->task_id); ++ sst_fill_header_mrfld(&msg->mrfld_header, IPC_CMD, ++ str_info->task_id, 1, pvt_id); ++ ++ len = sizeof(u16) + sizeof(dsp_hdr); ++ msg->mrfld_header.p.header_low_payload = len; ++ sst_fill_header_dsp(&dsp_hdr, IPC_IA_START_STREAM_MRFLD, ++ str_info->pipe_id, sizeof(u16)); ++ memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr)); ++ memset(msg->mailbox_data + sizeof(dsp_hdr), 0, sizeof(u16)); ++ trace_sst_stream("START ->", str_id, str_info->pipe_id); ++ pr_info("Start for str %d pipe %#x\n", str_id, str_info->pipe_id); ++ ++ } else { ++ pr_debug("fill START_STREAM for CTP\n"); ++ sst_fill_header(&msg->header, IPC_IA_START_STREAM, 1, str_id); ++ msg->header.part.data = sizeof(u32) + sizeof(u32); ++ memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); ++ memset(msg->mailbox_data + sizeof(u32), 0, sizeof(u32)); ++ } ++ sst_drv_ctx->ops->sync_post_message(msg); ++ return retval; ++} ++ ++int sst_send_byte_stream_mrfld(void *sbytes) ++{ ++ struct ipc_post *msg = NULL; ++ struct snd_sst_bytes_v2 *bytes = (struct snd_sst_bytes_v2 *) sbytes; ++ u32 length; ++ int pvt_id, ret = 0; ++ struct sst_block *block = NULL; ++ ++ pr_debug("%s: type:%u ipc_msg:%u block:%u task_id:%u pipe: %#x length:%#x\n", ++ __func__, bytes->type, bytes->ipc_msg, ++ bytes->block, bytes->task_id, ++ bytes->pipe_id, bytes->len); ++ ++ /* need some err check as this is user data, perhpas move this to the ++ * platform driver and pass the struct ++ */ ++ if (sst_create_ipc_msg(&msg, true)) ++ return -ENOMEM; ++ ++ pvt_id = sst_assign_pvt_id(sst_drv_ctx); ++ sst_fill_header_mrfld(&msg->mrfld_header, bytes->ipc_msg, bytes->task_id, ++ 1, pvt_id); ++ msg->mrfld_header.p.header_high.part.res_rqd = bytes->block; ++ length = bytes->len; ++ msg->mrfld_header.p.header_low_payload = length; ++ pr_debug("length is %d\n", length); ++ memcpy(msg->mailbox_data, &bytes->bytes, bytes->len); ++ trace_sst_stream("BYTES ->", bytes->type, bytes->pipe_id); ++ if (bytes->block) { ++ block = sst_create_block(sst_drv_ctx, bytes->ipc_msg, pvt_id); ++ if (block == NULL) { ++ kfree(msg); ++ return -ENOMEM; ++ } ++ } ++ sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); ++ pr_debug("msg->mrfld_header.p.header_low_payload:%d", msg->mrfld_header.p.header_low_payload); ++ if (bytes->block) { ++ ret = sst_wait_timeout(sst_drv_ctx, block); ++ if (ret) { ++ pr_err("%s: fw returned err %d\n", __func__, ret); ++ sst_free_block(sst_drv_ctx, block); ++ return ret; ++ } ++ } ++ if (bytes->type == SND_SST_BYTES_GET) { ++ /* copy the reply and send back ++ * we need to update only sz and payload ++ */ ++ if (bytes->block) { ++ unsigned char *r = block->data; ++ pr_debug("read back %d bytes", bytes->len); ++ memcpy(bytes->bytes, r, bytes->len); ++ trace_sst_stream("BYTES <-", bytes->type, bytes->pipe_id); ++ } ++ } ++ if (bytes->block) ++ sst_free_block(sst_drv_ctx, block); ++ return 0; ++} ++ ++int sst_send_probe_bytes(struct intel_sst_drv *sst) ++{ ++ struct ipc_post *msg = NULL; ++ struct sst_block *block; ++ int ret_val = 0; ++ ++ ret_val = sst_create_block_and_ipc_msg(&msg, true, sst, ++ &block, IPC_IA_DBG_SET_PROBE_PARAMS, 0); ++ if (ret_val) { ++ pr_err("Can't allocate block/msg: Probe Byte Stream\n"); ++ return ret_val; ++ } ++ ++ sst_fill_header(&msg->header, IPC_IA_DBG_SET_PROBE_PARAMS, 1, 0); ++ ++ msg->header.part.data = sizeof(u32) + sst->probe_bytes->len; ++ memcpy(msg->mailbox_data, &msg->header.full, sizeof(u32)); ++ memcpy(msg->mailbox_data + sizeof(u32), sst->probe_bytes->bytes, ++ sst->probe_bytes->len); ++ ++ sst_add_to_dispatch_list_and_post(sst, msg); ++ ret_val = sst_wait_timeout(sst, block); ++ sst_free_block(sst, block); ++ if (ret_val) ++ pr_err("set probe stream param..timeout!\n"); ++ return ret_val; ++} ++ ++/* ++ * sst_pause_stream - Send msg for a pausing stream ++ * @str_id: stream ID ++ * ++ * This function is called by any function which wants to pause ++ * an already running stream. ++ */ ++int sst_pause_stream(int str_id) ++{ ++ int retval = 0, pvt_id, len; ++ struct ipc_post *msg = NULL; ++ struct stream_info *str_info; ++ struct intel_sst_ops *ops; ++ struct sst_block *block; ++ struct ipc_dsp_hdr dsp_hdr; ++ ++ pr_debug("SST DBG:sst_pause_stream for %d\n", str_id); ++ str_info = get_stream_info(str_id); ++ if (!str_info) ++ return -EINVAL; ++ ops = sst_drv_ctx->ops; ++ if (str_info->status == STREAM_PAUSED) ++ return 0; ++ if (str_info->status == STREAM_RUNNING || ++ str_info->status == STREAM_INIT) { ++ if (str_info->prev == STREAM_UN_INIT) ++ return -EBADRQC; ++ if (!sst_drv_ctx->use_32bit_ops) { ++ pvt_id = sst_assign_pvt_id(sst_drv_ctx); ++ retval = sst_create_block_and_ipc_msg(&msg, true, ++ sst_drv_ctx, &block, IPC_CMD, pvt_id); ++ if (retval) ++ return retval; ++ sst_fill_header_mrfld(&msg->mrfld_header, IPC_CMD, ++ str_info->task_id, 1, pvt_id); ++ msg->mrfld_header.p.header_high.part.res_rqd = 1; ++ len = sizeof(dsp_hdr); ++ msg->mrfld_header.p.header_low_payload = len; ++ sst_fill_header_dsp(&dsp_hdr, IPC_IA_PAUSE_STREAM_MRFLD, ++ str_info->pipe_id, 0); ++ memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr)); ++ trace_sst_stream("PAUSE ->", str_id, str_info->pipe_id); ++ } else { ++ retval = sst_create_block_and_ipc_msg(&msg, false, ++ sst_drv_ctx, &block, ++ IPC_IA_PAUSE_STREAM, str_id); ++ if (retval) ++ return retval; ++ sst_fill_header(&msg->header, IPC_IA_PAUSE_STREAM, ++ 0, str_id); ++ } ++ sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); ++ retval = sst_wait_timeout(sst_drv_ctx, block); ++ sst_free_block(sst_drv_ctx, block); ++ if (retval == 0) { ++ str_info->prev = str_info->status; ++ str_info->status = STREAM_PAUSED; ++ } else if (retval == SST_ERR_INVALID_STREAM_ID) { ++ retval = -EINVAL; ++ mutex_lock(&sst_drv_ctx->stream_lock); ++ sst_clean_stream(str_info); ++ mutex_unlock(&sst_drv_ctx->stream_lock); ++ } ++ } else { ++ retval = -EBADRQC; ++ pr_debug("SST DBG:BADRQC for stream\n "); ++ } ++ ++ return retval; ++} ++ ++/** ++ * sst_resume_stream - Send msg for resuming stream ++ * @str_id: stream ID ++ * ++ * This function is called by any function which wants to resume ++ * an already paused stream. ++ */ ++int sst_resume_stream(int str_id) ++{ ++ int retval = 0; ++ struct ipc_post *msg = NULL; ++ struct stream_info *str_info; ++ struct intel_sst_ops *ops; ++ struct sst_block *block = NULL; ++ int pvt_id, len; ++ struct ipc_dsp_hdr dsp_hdr; ++ ++ pr_debug("SST DBG:sst_resume_stream for %d\n", str_id); ++ str_info = get_stream_info(str_id); ++ if (!str_info) ++ return -EINVAL; ++ ops = sst_drv_ctx->ops; ++ if (str_info->status == STREAM_RUNNING) ++ return 0; ++ if (str_info->status == STREAM_PAUSED) { ++ if (!sst_drv_ctx->use_32bit_ops) { ++ pvt_id = sst_assign_pvt_id(sst_drv_ctx); ++ retval = sst_create_block_and_ipc_msg(&msg, true, ++ sst_drv_ctx, &block, IPC_CMD, pvt_id); ++ if (retval) ++ return retval; ++ sst_fill_header_mrfld(&msg->mrfld_header, IPC_CMD, ++ str_info->task_id, 1, pvt_id); ++ msg->mrfld_header.p.header_high.part.res_rqd = 1; ++ len = sizeof(dsp_hdr); ++ msg->mrfld_header.p.header_low_payload = len; ++ sst_fill_header_dsp(&dsp_hdr, ++ IPC_IA_RESUME_STREAM_MRFLD, ++ str_info->pipe_id, 0); ++ memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr)); ++ trace_sst_stream("RESUME->", str_id, str_info->pipe_id); ++ } else { ++ retval = sst_create_block_and_ipc_msg(&msg, false, ++ sst_drv_ctx, &block, ++ IPC_IA_RESUME_STREAM, str_id); ++ if (retval) ++ return retval; ++ sst_fill_header(&msg->header, IPC_IA_RESUME_STREAM, ++ 0, str_id); ++ } ++ sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); ++ retval = sst_wait_timeout(sst_drv_ctx, block); ++ sst_free_block(sst_drv_ctx, block); ++ if (!retval) { ++ if (str_info->prev == STREAM_RUNNING) ++ str_info->status = STREAM_RUNNING; ++ else ++ str_info->status = STREAM_INIT; ++ str_info->prev = STREAM_PAUSED; ++ } else if (retval == -SST_ERR_INVALID_STREAM_ID) { ++ retval = -EINVAL; ++ mutex_lock(&sst_drv_ctx->stream_lock); ++ sst_clean_stream(str_info); ++ mutex_unlock(&sst_drv_ctx->stream_lock); ++ } ++ } else { ++ retval = -EBADRQC; ++ pr_err("SST ERR: BADQRC for stream\n"); ++ } ++ ++ return retval; ++} ++ ++ ++/** ++ * sst_drop_stream - Send msg for stopping stream ++ * @str_id: stream ID ++ * ++ * This function is called by any function which wants to stop ++ * a stream. ++ */ ++int sst_drop_stream(int str_id) ++{ ++ int retval = 0, pvt_id; ++ struct stream_info *str_info; ++ struct ipc_post *msg = NULL; ++ struct ipc_dsp_hdr dsp_hdr; ++ ++ pr_debug("SST DBG:sst_drop_stream for %d\n", str_id); ++ str_info = get_stream_info(str_id); ++ if (!str_info) ++ return -EINVAL; ++ ++ if (str_info->status != STREAM_UN_INIT) { ++ ++ if (sst_drv_ctx->use_32bit_ops == true) { ++ str_info->prev = STREAM_UN_INIT; ++ str_info->status = STREAM_INIT; ++ str_info->cumm_bytes = 0; ++ sst_send_sync_msg(IPC_IA_DROP_STREAM, str_id); ++ } else { ++ if (sst_create_ipc_msg(&msg, true)) ++ return -ENOMEM; ++ str_info->prev = STREAM_UN_INIT; ++ str_info->status = STREAM_INIT; ++ str_info->cumm_bytes = 0; ++ pvt_id = sst_assign_pvt_id(sst_drv_ctx); ++ sst_fill_header_mrfld(&msg->mrfld_header, IPC_CMD, ++ str_info->task_id, 1, pvt_id); ++ ++ msg->mrfld_header.p.header_low_payload = sizeof(dsp_hdr); ++ sst_fill_header_dsp(&dsp_hdr, IPC_IA_DROP_STREAM_MRFLD, ++ str_info->pipe_id, 0); ++ memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr)); ++ trace_sst_stream("STOP ->", str_id, str_info->pipe_id); ++ pr_info("Stop for str %d pipe %#x\n", str_id, str_info->pipe_id); ++ ++ sst_drv_ctx->ops->sync_post_message(msg); ++ } ++ } else { ++ retval = -EBADRQC; ++ pr_debug("BADQRC for stream, state %x\n", str_info->status); ++ } ++ return retval; ++} ++ ++/** ++ * sst_next_track: notify next track ++ * @str_id: stream ID ++ * ++ * This function is called by any function which wants to ++ * set next track. Current this is NOP as FW doest care ++ */ ++int sst_next_track(void) ++{ ++ pr_debug("SST DBG: next_track"); ++ return 0; ++} ++ ++/** ++* sst_drain_stream - Send msg for draining stream ++* @str_id: stream ID ++* ++* This function is called by any function which wants to drain ++* a stream. ++*/ ++int sst_drain_stream(int str_id, bool partial_drain) ++{ ++ int retval = 0, pvt_id, len; ++ struct ipc_post *msg = NULL; ++ struct stream_info *str_info; ++ struct intel_sst_ops *ops; ++ struct sst_block *block = NULL; ++ struct ipc_dsp_hdr dsp_hdr; ++ ++ pr_debug("SST DBG:sst_drain_stream for %d\n", str_id); ++ str_info = get_stream_info(str_id); ++ if (!str_info) ++ return -EINVAL; ++ ops = sst_drv_ctx->ops; ++ if (str_info->status != STREAM_RUNNING && ++ str_info->status != STREAM_INIT && ++ str_info->status != STREAM_PAUSED) { ++ pr_err("SST ERR: BADQRC for stream = %d\n", ++ str_info->status); ++ return -EBADRQC; ++ } ++ ++ if (!sst_drv_ctx->use_32bit_ops) { ++ pvt_id = sst_assign_pvt_id(sst_drv_ctx); ++ retval = sst_create_block_and_ipc_msg(&msg, true, ++ sst_drv_ctx, &block, IPC_CMD, pvt_id); ++ if (retval) ++ return retval; ++ sst_fill_header_mrfld(&msg->mrfld_header, IPC_CMD, ++ str_info->task_id, 1, pvt_id); ++ pr_debug("header:%x\n", ++ (unsigned int)msg->mrfld_header.p.header_high.full); ++ msg->mrfld_header.p.header_high.part.res_rqd = 1; ++ ++ len = sizeof(u8) + sizeof(dsp_hdr); ++ msg->mrfld_header.p.header_low_payload = len; ++ sst_fill_header_dsp(&dsp_hdr, IPC_IA_DRAIN_STREAM_MRFLD, ++ str_info->pipe_id, sizeof(u8)); ++ memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr)); ++ memcpy(msg->mailbox_data + sizeof(dsp_hdr), ++ &partial_drain, sizeof(u8)); ++ trace_sst_stream("DRAIN ->", str_id, str_info->pipe_id); ++ } else { ++ retval = sst_create_block_and_ipc_msg(&msg, false, ++ sst_drv_ctx, &block, ++ IPC_IA_DRAIN_STREAM, str_id); ++ if (retval) ++ return retval; ++ sst_fill_header(&msg->header, IPC_IA_DRAIN_STREAM, 0, str_id); ++ msg->header.part.data = partial_drain; ++ } ++ sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); ++ /* with new non blocked drain implementation in core we dont need to ++ * wait for respsonse, and need to only invoke callback for drain ++ * complete ++ */ ++ ++ sst_free_block(sst_drv_ctx, block); ++ return retval; ++} ++ ++/** ++ * sst_free_stream - Frees a stream ++ * @str_id: stream ID ++ * ++ * This function is called by any function which wants to free ++ * a stream. ++ */ ++int sst_free_stream(int str_id) ++{ ++ int retval = 0; ++ unsigned int pvt_id; ++ struct ipc_post *msg = NULL; ++ struct stream_info *str_info; ++ struct intel_sst_ops *ops; ++ unsigned long irq_flags; ++ struct ipc_dsp_hdr dsp_hdr; ++ struct sst_block *block; ++ ++ pr_debug("SST DBG:sst_free_stream for %d\n", str_id); ++ ++ mutex_lock(&sst_drv_ctx->sst_lock); ++ if (sst_drv_ctx->sst_state == SST_UN_INIT) { ++ mutex_unlock(&sst_drv_ctx->sst_lock); ++ return -ENODEV; ++ } ++ mutex_unlock(&sst_drv_ctx->sst_lock); ++ str_info = get_stream_info(str_id); ++ if (!str_info) ++ return -EINVAL; ++ ops = sst_drv_ctx->ops; ++ ++ mutex_lock(&str_info->lock); ++ if (str_info->status != STREAM_UN_INIT) { ++ str_info->prev = str_info->status; ++ str_info->status = STREAM_UN_INIT; ++ mutex_unlock(&str_info->lock); ++ ++ if (!sst_drv_ctx->use_32bit_ops) { ++ pvt_id = sst_assign_pvt_id(sst_drv_ctx); ++ retval = sst_create_block_and_ipc_msg(&msg, true, ++ sst_drv_ctx, &block, IPC_CMD, pvt_id); ++ if (retval) ++ return retval; ++ ++ sst_fill_header_mrfld(&msg->mrfld_header, IPC_CMD, ++ str_info->task_id, 1, pvt_id); ++ msg->mrfld_header.p.header_low_payload = ++ sizeof(dsp_hdr); ++ sst_fill_header_dsp(&dsp_hdr, IPC_IA_FREE_STREAM_MRFLD, ++ str_info->pipe_id, 0); ++ memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr)); ++ trace_sst_stream("FREE ->", str_id, str_info->pipe_id); ++ pr_info("Free for str %d pipe %#x\n", str_id, str_info->pipe_id); ++ ++ } else { ++ retval = sst_create_block_and_ipc_msg(&msg, false, ++ sst_drv_ctx, &block, ++ IPC_IA_FREE_STREAM, str_id); ++ if (retval) ++ return retval; ++ sst_fill_header(&msg->header, IPC_IA_FREE_STREAM, ++ 0, str_id); ++ } ++ spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags); ++ list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); ++ spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); ++ if (!sst_drv_ctx->use_32bit_ops) { ++ /*FIXME: do we need to wake up drain stream here, ++ * how to get the pvt_id and msg_id ++ */ ++ } else { ++ sst_wake_up_block(sst_drv_ctx, 0, str_id, ++ IPC_IA_DRAIN_STREAM, NULL, 0); ++ } ++ ops->post_message(&sst_drv_ctx->ipc_post_msg_wq); ++ retval = sst_wait_timeout(sst_drv_ctx, block); ++ pr_debug("sst: wait for free returned %d\n", retval); ++ mutex_lock(&sst_drv_ctx->stream_lock); ++ sst_clean_stream(str_info); ++ mutex_unlock(&sst_drv_ctx->stream_lock); ++ pr_debug("SST DBG:Stream freed\n"); ++ sst_free_block(sst_drv_ctx, block); ++ } else { ++ mutex_unlock(&str_info->lock); ++ retval = -EBADRQC; ++ pr_debug("SST DBG:BADQRC for stream\n"); ++ } ++ ++ return retval; ++} ++ ++int sst_request_vtsv_file(char *fname, struct intel_sst_drv *ctx, ++ void **out_file, u32 *out_size) ++{ ++ int retval = 0; ++ const struct firmware *file; ++ void *ddr_virt_addr; ++ unsigned long file_base; ++ ++ if (!ctx->pdata->lib_info) { ++ pr_err("lib_info pointer NULL\n"); ++ return -EINVAL; ++ } ++ ++ pr_debug("Requesting VTSV file %s now...\n", fname); ++ retval = request_firmware(&file, fname, ctx->dev); ++ if (file == NULL) { ++ pr_err("VTSV file is returning as null\n"); ++ return -EINVAL; ++ } ++ if (retval) { ++ pr_err("request fw failed %d\n", retval); ++ return retval; ++ } ++ ++ if ((*out_file == NULL) || (*out_size < file->size)) { ++ retval = sst_get_next_lib_mem(&ctx->lib_mem_mgr, file->size, ++ &file_base); ++ *out_file = (void *)file_base; ++ } ++ ddr_virt_addr = (unsigned char *)ctx->ddr + ++ (unsigned long)(*out_file - ctx->pdata->lib_info->mod_base); ++ memcpy(ddr_virt_addr, file->data, file->size); ++ ++ *out_size = file->size; ++ release_firmware(file); ++ return 0; ++} ++ ++int sst_format_vtsv_message(struct intel_sst_drv *ctx, ++ struct ipc_post **msgptr, struct sst_block **block) ++{ ++ int retval = 0, pvt_id, len; ++ struct ipc_dsp_hdr dsp_hdr; ++ struct snd_sst_vtsv_info vinfo; ++ struct ipc_post *msg; ++ ++ BUG_ON((unsigned long)(ctx->vcache.file1_in_mem) & 0xffffffff00000000ULL); ++ BUG_ON((unsigned long)(ctx->vcache.file2_in_mem) & 0xffffffff00000000ULL); ++ ++ vinfo.vfiles[0].addr = (u32)((unsigned long)ctx->vcache.file1_in_mem ++ & 0xffffffff); ++ vinfo.vfiles[0].size = ctx->vcache.size1; ++ vinfo.vfiles[1].addr = (u32)((unsigned long)ctx->vcache.file2_in_mem ++ & 0xffffffff); ++ vinfo.vfiles[1].size = ctx->vcache.size2; ++ ++ /* Create the vtsv message */ ++ pvt_id = sst_assign_pvt_id(ctx); ++ retval = sst_create_block_and_ipc_msg(msgptr, true, ++ ctx, block, IPC_CMD, pvt_id); ++ if (retval) ++ return retval; ++ msg = *msgptr; ++ sst_fill_header_mrfld(&msg->mrfld_header, IPC_CMD, ++ SST_TASK_ID_AWARE, 1, pvt_id); ++ pr_debug("header:%x\n", ++ (unsigned int)msg->mrfld_header.p.header_high.full); ++ msg->mrfld_header.p.header_high.part.res_rqd = 1; ++ ++ len = sizeof(vinfo) + sizeof(dsp_hdr); ++ msg->mrfld_header.p.header_low_payload = len; ++ sst_fill_header_dsp(&dsp_hdr, IPC_IA_VTSV_UPDATE_MODULES, ++ PIPE_VAD_OUT, sizeof(u8)); ++ dsp_hdr.mod_id = SST_ALGO_VTSV; ++ memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr)); ++ memcpy(msg->mailbox_data + sizeof(dsp_hdr), ++ &vinfo, sizeof(vinfo)); ++ return 0; ++} ++ ++int sst_send_vtsv_data_to_fw(struct intel_sst_drv *ctx) ++{ ++ int retval = 0; ++ struct ipc_post *msg = NULL; ++ struct sst_block *block = NULL; ++ ++ /* Download both the data files */ ++ retval = sst_request_vtsv_file("vtsv_net.bin", ctx, ++ &ctx->vcache.file1_in_mem, &ctx->vcache.size1); ++ if (retval) { ++ pr_err("vtsv data file1 request failed %d\n", retval); ++ return retval; ++ } ++ ++ retval = sst_request_vtsv_file("vtsv_grammar.bin", ctx, ++ &ctx->vcache.file2_in_mem, &ctx->vcache.size2); ++ if (retval) { ++ pr_err("vtsv data file2 request failed %d\n", retval); ++ return retval; ++ } ++ ++ retval = sst_format_vtsv_message(ctx, &msg, &block); ++ if (retval) { ++ pr_err("vtsv msg format failed %d\n", retval); ++ return retval; ++ } ++ sst_add_to_dispatch_list_and_post(ctx, msg); ++ retval = sst_wait_timeout(ctx, block); ++ if (retval) ++ pr_err("vtsv msg send to fw failed %d\n", retval); ++ ++ sst_free_block(ctx, block); ++ return retval; ++} +diff --git a/sound/soc/intel/sst/sst_trace.h b/sound/soc/intel/sst/sst_trace.h +new file mode 100644 +index 0000000..8d34ef3 +--- /dev/null ++++ b/sound/soc/intel/sst/sst_trace.h +@@ -0,0 +1,147 @@ ++/* ++ * sst_trace.h - Intel SST Driver tracing support ++ * ++ * Copyright (C) 2013 Intel Corp ++ * Authors: Omair Mohammed Abdullah ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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. ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ */ ++ ++#undef TRACE_SYSTEM ++#define TRACE_SYSTEM sst ++ ++#if !defined(_TRACE_SST_H) || defined(TRACE_HEADER_MULTI_READ) ++#define _TRACE_SST_H ++ ++#include ++#include ++#include ++ ++TRACE_EVENT(sst_ipc, ++ ++ TP_PROTO(const char *msg, u32 header_high, u32 header_low, int pvt_id), ++ ++ TP_ARGS(msg, header_high, header_low, pvt_id), ++ ++ TP_STRUCT__entry( ++ __string(info_msg, msg) ++ __field(unsigned int, val_l) ++ __field(unsigned int, val_h) ++ __field(unsigned int, id) ++ ), ++ ++ TP_fast_assign( ++ __assign_str(info_msg, msg); ++ __entry->val_l = header_low; ++ __entry->val_h = header_high; ++ __entry->id = pvt_id; ++ ), ++ ++ TP_printk("\t%s\t [%2u] = %#8.8x:%.4x", __get_str(info_msg), ++ (unsigned int)__entry->id, ++ (unsigned int)__entry->val_h, (unsigned int)__entry->val_l) ++ ++); ++ ++TRACE_EVENT(sst_stream, ++ ++ TP_PROTO(const char *msg, int str_id, int pipe_id), ++ ++ TP_ARGS(msg, str_id, pipe_id), ++ ++ TP_STRUCT__entry( ++ __string(info_msg, msg) ++ __field(unsigned int, str_id) ++ __field(unsigned int, pipe_id) ++ ), ++ ++ TP_fast_assign( ++ __assign_str(info_msg, msg); ++ __entry->str_id = str_id; ++ __entry->pipe_id = pipe_id; ++ ), ++ ++ TP_printk("\t%s\t str = %2u, pipe = %#x", __get_str(info_msg), ++ (unsigned int)__entry->str_id, (unsigned int)__entry->pipe_id) ++); ++ ++TRACE_EVENT(sst_ipc_mailbox, ++ ++ TP_PROTO(const char *mailbox, int mbox_len), ++ ++ TP_ARGS(mailbox, mbox_len), ++ ++ TP_STRUCT__entry( ++ __dynamic_array(char, mbox, (3 * mbox_len)) ++ ), ++ ++ TP_fast_assign( ++ sst_dump_to_buffer(mailbox, mbox_len, ++ __get_dynamic_array(mbox)); ++ ), ++ ++ TP_printk(" %s", __get_str(mbox)) ++ ++); ++ ++TRACE_EVENT(sst_lib_download, ++ ++ TP_PROTO(const char *msg, const char *lib_name), ++ ++ TP_ARGS(msg, lib_name), ++ ++ TP_STRUCT__entry( ++ __string(info_msg, msg) ++ __string(info_lib_name, lib_name) ++ ), ++ ++ TP_fast_assign( ++ __assign_str(info_msg, msg); ++ __assign_str(info_lib_name, lib_name); ++ ), ++ ++ TP_printk("\t%s %s", __get_str(info_msg), ++ __get_str(info_lib_name)) ++); ++ ++TRACE_EVENT(sst_fw_download, ++ ++ TP_PROTO(const char *msg, int fw_state), ++ ++ TP_ARGS(msg, fw_state), ++ ++ TP_STRUCT__entry( ++ __string(info_msg, msg) ++ __field(unsigned int, fw_state) ++ ), ++ ++ TP_fast_assign( ++ __assign_str(info_msg, msg); ++ __entry->fw_state = fw_state; ++ ), ++ ++ TP_printk("\t%s\tFW state = %d", __get_str(info_msg), ++ (unsigned int)__entry->fw_state) ++); ++ ++#endif /* _TRACE_SST_H */ ++ ++/* This part must be outside protection */ ++#undef TRACE_INCLUDE_PATH ++#define TRACE_INCLUDE_PATH . ++#define TRACE_INCLUDE_FILE sst_trace ++#include +diff --git a/sound/soc/intel/sst_platform.h b/sound/soc/intel/sst_platform.h +new file mode 100644 +index 0000000..5d231b5 +--- /dev/null ++++ b/sound/soc/intel/sst_platform.h +@@ -0,0 +1,155 @@ ++/* ++ * sst_platform.h - Intel MID Platform driver header file ++ * ++ * Copyright (C) 2010 Intel Corp ++ * Author: Vinod Koul ++ * Author: Harsha Priya ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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_PLATFORM_H__ ++#define __SST_PLATFORM_H__ ++ ++#include ++ ++#define SST_MAX_BIN_BYTES 1024 ++ ++struct sst_data; ++ ++enum sst_audio_device_type { ++ SND_SST_DEVICE_HEADSET = 1, ++ SND_SST_DEVICE_IHF, ++ SND_SST_DEVICE_VIBRA, ++ SND_SST_DEVICE_HAPTIC, ++ SND_SST_DEVICE_CAPTURE, ++ SND_SST_DEVICE_COMPRESS, ++}; ++ ++enum snd_sst_input_stream { ++ SST_INPUT_STREAM_NONE = 0x0, ++ SST_INPUT_STREAM_PCM = 0x6, ++ SST_INPUT_STREAM_COMPRESS = 0x8, ++ SST_INPUT_STREAM_MIXED = 0xE, ++}; ++ ++enum sst_stream_ops { ++ STREAM_OPS_PLAYBACK = 0, /* Decode */ ++ STREAM_OPS_CAPTURE, /* Encode */ ++ STREAM_OPS_COMPRESSED_PATH, /* Offload playback/capture */ ++ ++}; ++enum snd_sst_stream_type { ++ SST_STREAM_DEVICE_HS = 32, ++ SST_STREAM_DEVICE_IHF = 33, ++ SST_STREAM_DEVICE_MIC0 = 34, ++ SST_STREAM_DEVICE_MIC1 = 35, ++}; ++ ++enum sst_controls { ++ SST_SND_ALLOC = 0x1000, ++ SST_SND_PAUSE = 0x1001, ++ SST_SND_RESUME = 0x1002, ++ SST_SND_DROP = 0x1003, ++ SST_SND_FREE = 0x1004, ++ SST_SND_BUFFER_POINTER = 0x1005, ++ SST_SND_STREAM_INIT = 0x1006, ++ SST_SND_START = 0x1007, ++ SST_SET_RUNTIME_PARAMS = 0x1008, ++ SST_SET_ALGO_PARAMS = 0x1009, ++ SST_SET_BYTE_STREAM = 0x100A, ++ SST_GET_BYTE_STREAM = 0x100B, ++ SST_SET_SSP_CONFIG = 0x100C, ++ SST_SET_PROBE_BYTE_STREAM = 0x100D, ++ SST_GET_PROBE_BYTE_STREAM = 0x100E, ++ SST_SET_VTSV_INFO = 0x100F, ++}; ++ ++struct pcm_stream_info { ++ int str_id; ++ void *mad_substream; ++ void (*period_elapsed) (void *mad_substream); ++ unsigned long long buffer_ptr; ++ unsigned long long pcm_delay; ++ int sfreq; ++}; ++ ++struct sst_compress_cb { ++ void *param; ++ void (*compr_cb)(void *param); ++ void *drain_cb_param; ++ void (*drain_notify)(void *param); ++ ++}; ++ ++struct snd_sst_params; ++ ++struct compress_sst_ops { ++ const char *name; ++ int (*open) (struct snd_sst_params *str_params, ++ struct sst_compress_cb *cb); ++ int (*control) (unsigned int cmd, unsigned int str_id); ++ int (*tstamp) (unsigned int str_id, struct snd_compr_tstamp *tstamp); ++ int (*ack) (unsigned int str_id, unsigned long bytes); ++ int (*close) (unsigned int str_id); ++ int (*get_caps) (struct snd_compr_caps *caps); ++ int (*get_codec_caps) (struct snd_compr_codec_caps *codec); ++ int (*set_metadata) (unsigned int str_id, struct snd_compr_metadata *metadata); ++ ++}; ++ ++enum lpe_param_types_mixer { ++ SST_ALGO_PARAM_MIXER_STREAM_CFG = 0x801, ++}; ++ ++struct mad_ops_wq { ++ int stream_id; ++ enum sst_controls control_op; ++ struct work_struct wq; ++}; ++ ++struct sst_ops { ++ int (*open) (struct snd_sst_params *str_param); ++ int (*device_control) (int cmd, void *arg); ++ int (*set_generic_params) (enum sst_controls cmd, void *arg); ++ int (*close) (unsigned int str_id); ++ int (*power) (bool state); ++}; ++ ++struct sst_runtime_stream { ++ int stream_status; ++ unsigned int id; ++ size_t bytes_written; ++ struct pcm_stream_info stream_info; ++ struct sst_ops *ops; ++ struct compress_sst_ops *compr_ops; ++ spinlock_t status_lock; ++}; ++ ++struct sst_device { ++ char *name; ++ struct device *dev; ++ struct sst_ops *ops; ++ struct platform_device *pdev; ++ struct compress_sst_ops *compr_ops; ++}; ++ ++int sst_register_dsp(struct sst_device *sst); ++int sst_unregister_dsp(struct sst_device *sst); ++#endif +diff --git a/sound/soc/intel/sst_platform_pvt.h b/sound/soc/intel/sst_platform_pvt.h +new file mode 100644 +index 0000000..f74dd9c +--- /dev/null ++++ b/sound/soc/intel/sst_platform_pvt.h +@@ -0,0 +1,127 @@ ++/* ++ * sst_platform_pvt.h - Intel MID Platform driver header file ++ * ++ * Copyright (C) 2010 Intel Corp ++ * Author: Vinod Koul ++ * Author: Harsha Priya ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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. ++ * ++ * 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_PLATFORM_PVT_H__ ++#define __SST_PLATFORM_PVT_H__ ++ ++/* TODO rmv this global */ ++extern struct sst_device *sst_dsp; ++ ++#define SST_MONO 1 ++#define SST_STEREO 2 ++ ++#define SST_MIN_RATE 8000 ++#define SST_MAX_RATE 48000 ++#define SST_MIN_CHANNEL 1 ++#define SST_MAX_CHANNEL 2 ++ ++#define SST_MAX_BUFFER 96000 /*500ms@48K,16bit,2ch - CLV*/ ++#define SST_MIN_PERIOD_BYTES 1536 /*24ms@16K,16bit,2ch - For VoIP on Mrfld*/ ++#define SST_MAX_PERIOD_BYTES 48000 /*250ms@48K,16bit,2ch - CLV*/ ++ ++#define SST_MIN_PERIODS 2 ++#define SST_MAX_PERIODS 50 ++#define SST_FIFO_SIZE 0 ++#define SST_CODEC_TYPE_PCM 1 ++ ++#define SST_HEADSET_DAI "Headset-cpu-dai" ++#define SST_SPEAKER_DAI "Speaker-cpu-dai" ++#define SST_VOICE_DAI "Voice-cpu-dai" ++#define SST_VIRTUAL_DAI "Virtual-cpu-dai" ++#define SST_LOOPBACK_DAI "Loopback-cpu-dai" ++#define SST_POWER_DAI "Power-cpu-dai" ++#define SST_COMPRESS_DAI "Compress-cpu-dai" ++#define SST_PROBE_DAI "Probe-cpu-dai" ++#define SST_VOIP_DAI "Voip-cpu-dai" ++#define SST_DEEPBUFFER_DAI "Deepbuffer-cpu-dai" ++#define SST_LOWLATENCY_DAI "Lowlatency-cpu-dai" ++ ++struct sst_device; ++ ++enum sst_drv_status { ++ SST_PLATFORM_UNINIT, ++ SST_PLATFORM_INIT, ++ SST_PLATFORM_RUNNING, ++ SST_PLATFORM_PAUSED, ++ SST_PLATFORM_DROPPED, ++}; ++ ++#define SST_PIPE_CONTROL 0x0 ++#define SST_COMPRESS_VOL 0x01 ++ ++int sst_platform_clv_init(struct snd_soc_platform *platform); ++int sst_dsp_init(struct snd_soc_platform *platform); ++int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform); ++int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute); ++ ++unsigned int sst_soc_read(struct snd_soc_platform *platform, unsigned int reg); ++int sst_soc_write(struct snd_soc_platform *platform, unsigned int reg, unsigned int val); ++unsigned int sst_reg_read(struct sst_data *sst, unsigned int reg, ++ unsigned int shift, unsigned int max); ++unsigned int sst_reg_write(struct sst_data *sst, unsigned int reg, ++ unsigned int shift, unsigned int max, unsigned int val); ++ ++int sst_algo_int_ctl_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo); ++void sst_set_stream_status(struct sst_runtime_stream *stream, int state); ++int sst_fill_stream_params(void *substream, const struct sst_data *ctx, ++ struct snd_sst_params *str_params, bool is_compress); ++int sst_dpcm_probe_send(struct snd_soc_platform *platform, u16 probe_pipe, ++ int substream, int direction, bool on); ++int sst_byte_control_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol); ++int sst_byte_control_set(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol); ++ ++struct sst_algo_int_control_v2 { ++ struct soc_mixer_control mc; ++ u16 module_id; /* module identifieer */ ++ u16 pipe_id; /* location info: pipe_id + instance_id */ ++ u16 instance_id; ++ unsigned int value; /* Value received is stored here */ ++}; ++ ++struct sst_lowlatency_deepbuff { ++ /* Thresholds for low latency & deep buffer */ ++ unsigned long *low_latency; ++ unsigned long *deep_buffer; ++ unsigned long period_time; ++}; ++ ++struct sst_data { ++ struct platform_device *pdev; ++ struct sst_platform_data *pdata; ++ unsigned int lpe_mixer_input_ihf; ++ unsigned int lpe_mixer_input_hs; ++ u32 *widget; ++ char *byte_stream; ++ struct mutex lock; ++ /* Pipe_id for probe_stream to be saved in stream map */ ++ u8 pipe_id; ++ bool vtsv_enroll; ++ struct sst_lowlatency_deepbuff ll_db; ++}; ++#endif +diff --git a/sound/soc/mid-x86/Kconfig b/sound/soc/mid-x86/Kconfig +deleted file mode 100644 +index 61c10bf..0000000 +--- a/sound/soc/mid-x86/Kconfig ++++ /dev/null +@@ -1,13 +0,0 @@ +-config SND_MFLD_MACHINE +- tristate "SOC Machine Audio driver for Intel Medfield MID platform" +- depends on INTEL_SCU_IPC +- select SND_SOC_SN95031 +- select SND_SST_PLATFORM +- help +- This adds support for ASoC machine driver for Intel(R) MID Medfield platform +- used as alsa device in audio substem in Intel(R) MID devices +- Say Y if you have such a device +- If unsure select "N". +- +-config SND_SST_PLATFORM +- tristate +diff --git a/sound/soc/mid-x86/Makefile b/sound/soc/mid-x86/Makefile +deleted file mode 100644 +index 6398833..0000000 +--- a/sound/soc/mid-x86/Makefile ++++ /dev/null +@@ -1,5 +0,0 @@ +-snd-soc-sst-platform-objs := sst_platform.o +-snd-soc-mfld-machine-objs := mfld_machine.o +- +-obj-$(CONFIG_SND_SST_PLATFORM) += snd-soc-sst-platform.o +-obj-$(CONFIG_SND_MFLD_MACHINE) += snd-soc-mfld-machine.o +diff --git a/sound/soc/mid-x86/mfld_machine.c b/sound/soc/mid-x86/mfld_machine.c +deleted file mode 100644 +index 4139116..0000000 +--- a/sound/soc/mid-x86/mfld_machine.c ++++ /dev/null +@@ -1,447 +0,0 @@ +-/* +- * mfld_machine.c - ASoc Machine driver for Intel Medfield MID platform +- * +- * Copyright (C) 2010 Intel Corp +- * Author: Vinod Koul +- * Author: Harsha Priya +- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +- * +- * 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. +- * +- * 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 +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include "../codecs/sn95031.h" +- +-#define MID_MONO 1 +-#define MID_STEREO 2 +-#define MID_MAX_CAP 5 +-#define MFLD_JACK_INSERT 0x04 +- +-enum soc_mic_bias_zones { +- MFLD_MV_START = 0, +- /* mic bias volutage range for Headphones*/ +- MFLD_MV_HP = 400, +- /* mic bias volutage range for American Headset*/ +- MFLD_MV_AM_HS = 650, +- /* mic bias volutage range for Headset*/ +- MFLD_MV_HS = 2000, +- MFLD_MV_UNDEFINED, +-}; +- +-static unsigned int hs_switch; +-static unsigned int lo_dac; +- +-struct mfld_mc_private { +- void __iomem *int_base; +- u8 interrupt_status; +-}; +- +-struct snd_soc_jack mfld_jack; +- +-/*Headset jack detection DAPM pins */ +-static struct snd_soc_jack_pin mfld_jack_pins[] = { +- { +- .pin = "Headphones", +- .mask = SND_JACK_HEADPHONE, +- }, +- { +- .pin = "AMIC1", +- .mask = SND_JACK_MICROPHONE, +- }, +-}; +- +-/* jack detection voltage zones */ +-static struct snd_soc_jack_zone mfld_zones[] = { +- {MFLD_MV_START, MFLD_MV_AM_HS, SND_JACK_HEADPHONE}, +- {MFLD_MV_AM_HS, MFLD_MV_HS, SND_JACK_HEADSET}, +-}; +- +-/* sound card controls */ +-static const char *headset_switch_text[] = {"Earpiece", "Headset"}; +- +-static const char *lo_text[] = {"Vibra", "Headset", "IHF", "None"}; +- +-static const struct soc_enum headset_enum = +- SOC_ENUM_SINGLE_EXT(2, headset_switch_text); +- +-static const struct soc_enum lo_enum = +- SOC_ENUM_SINGLE_EXT(4, lo_text); +- +-static int headset_get_switch(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) +-{ +- ucontrol->value.integer.value[0] = hs_switch; +- return 0; +-} +- +-static int headset_set_switch(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); +- +- if (ucontrol->value.integer.value[0] == hs_switch) +- return 0; +- +- if (ucontrol->value.integer.value[0]) { +- pr_debug("hs_set HS path\n"); +- snd_soc_dapm_enable_pin(&codec->dapm, "Headphones"); +- snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT"); +- } else { +- pr_debug("hs_set EP path\n"); +- snd_soc_dapm_disable_pin(&codec->dapm, "Headphones"); +- snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT"); +- } +- snd_soc_dapm_sync(&codec->dapm); +- hs_switch = ucontrol->value.integer.value[0]; +- +- return 0; +-} +- +-static void lo_enable_out_pins(struct snd_soc_codec *codec) +-{ +- snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTL"); +- snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTR"); +- snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTL"); +- snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTR"); +- snd_soc_dapm_enable_pin(&codec->dapm, "VIB1OUT"); +- snd_soc_dapm_enable_pin(&codec->dapm, "VIB2OUT"); +- if (hs_switch) { +- snd_soc_dapm_enable_pin(&codec->dapm, "Headphones"); +- snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT"); +- } else { +- snd_soc_dapm_disable_pin(&codec->dapm, "Headphones"); +- snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT"); +- } +-} +- +-static int lo_get_switch(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) +-{ +- ucontrol->value.integer.value[0] = lo_dac; +- return 0; +-} +- +-static int lo_set_switch(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); +- +- if (ucontrol->value.integer.value[0] == lo_dac) +- return 0; +- +- /* we dont want to work with last state of lineout so just enable all +- * pins and then disable pins not required +- */ +- lo_enable_out_pins(codec); +- switch (ucontrol->value.integer.value[0]) { +- case 0: +- pr_debug("set vibra path\n"); +- snd_soc_dapm_disable_pin(&codec->dapm, "VIB1OUT"); +- snd_soc_dapm_disable_pin(&codec->dapm, "VIB2OUT"); +- snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0); +- break; +- +- case 1: +- pr_debug("set hs path\n"); +- snd_soc_dapm_disable_pin(&codec->dapm, "Headphones"); +- snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT"); +- snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x22); +- break; +- +- case 2: +- pr_debug("set spkr path\n"); +- snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTL"); +- snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTR"); +- snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x44); +- break; +- +- case 3: +- pr_debug("set null path\n"); +- snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTL"); +- snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTR"); +- snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x66); +- break; +- } +- snd_soc_dapm_sync(&codec->dapm); +- lo_dac = ucontrol->value.integer.value[0]; +- return 0; +-} +- +-static const struct snd_kcontrol_new mfld_snd_controls[] = { +- SOC_ENUM_EXT("Playback Switch", headset_enum, +- headset_get_switch, headset_set_switch), +- SOC_ENUM_EXT("Lineout Mux", lo_enum, +- lo_get_switch, lo_set_switch), +-}; +- +-static const struct snd_soc_dapm_widget mfld_widgets[] = { +- SND_SOC_DAPM_HP("Headphones", NULL), +- SND_SOC_DAPM_MIC("Mic", NULL), +-}; +- +-static const struct snd_soc_dapm_route mfld_map[] = { +- {"Headphones", NULL, "HPOUTR"}, +- {"Headphones", NULL, "HPOUTL"}, +- {"Mic", NULL, "AMIC1"}, +-}; +- +-static void mfld_jack_check(unsigned int intr_status) +-{ +- struct mfld_jack_data jack_data; +- +- jack_data.mfld_jack = &mfld_jack; +- jack_data.intr_id = intr_status; +- +- sn95031_jack_detection(&jack_data); +- /* TODO: add american headset detection post gpiolib support */ +-} +- +-static int mfld_init(struct snd_soc_pcm_runtime *runtime) +-{ +- struct snd_soc_codec *codec = runtime->codec; +- struct snd_soc_dapm_context *dapm = &codec->dapm; +- int ret_val; +- +- /* Add jack sense widgets */ +- snd_soc_dapm_new_controls(dapm, mfld_widgets, ARRAY_SIZE(mfld_widgets)); +- +- /* Set up the map */ +- snd_soc_dapm_add_routes(dapm, mfld_map, ARRAY_SIZE(mfld_map)); +- +- /* always connected */ +- snd_soc_dapm_enable_pin(dapm, "Headphones"); +- snd_soc_dapm_enable_pin(dapm, "Mic"); +- +- ret_val = snd_soc_add_codec_controls(codec, mfld_snd_controls, +- ARRAY_SIZE(mfld_snd_controls)); +- if (ret_val) { +- pr_err("soc_add_controls failed %d", ret_val); +- return ret_val; +- } +- /* default is earpiece pin, userspace sets it explcitly */ +- snd_soc_dapm_disable_pin(dapm, "Headphones"); +- /* default is lineout NC, userspace sets it explcitly */ +- snd_soc_dapm_disable_pin(dapm, "LINEOUTL"); +- snd_soc_dapm_disable_pin(dapm, "LINEOUTR"); +- lo_dac = 3; +- hs_switch = 0; +- /* we dont use linein in this so set to NC */ +- snd_soc_dapm_disable_pin(dapm, "LINEINL"); +- snd_soc_dapm_disable_pin(dapm, "LINEINR"); +- +- /* Headset and button jack detection */ +- ret_val = snd_soc_jack_new(codec, "Intel(R) MID Audio Jack", +- SND_JACK_HEADSET | SND_JACK_BTN_0 | +- SND_JACK_BTN_1, &mfld_jack); +- if (ret_val) { +- pr_err("jack creation failed\n"); +- return ret_val; +- } +- +- ret_val = snd_soc_jack_add_pins(&mfld_jack, +- ARRAY_SIZE(mfld_jack_pins), mfld_jack_pins); +- if (ret_val) { +- pr_err("adding jack pins failed\n"); +- return ret_val; +- } +- ret_val = snd_soc_jack_add_zones(&mfld_jack, +- ARRAY_SIZE(mfld_zones), mfld_zones); +- if (ret_val) { +- pr_err("adding jack zones failed\n"); +- return ret_val; +- } +- +- /* we want to check if anything is inserted at boot, +- * so send a fake event to codec and it will read adc +- * to find if anything is there or not */ +- mfld_jack_check(MFLD_JACK_INSERT); +- return ret_val; +-} +- +-static struct snd_soc_dai_link mfld_msic_dailink[] = { +- { +- .name = "Medfield Headset", +- .stream_name = "Headset", +- .cpu_dai_name = "Headset-cpu-dai", +- .codec_dai_name = "SN95031 Headset", +- .codec_name = "sn95031", +- .platform_name = "sst-platform", +- .init = mfld_init, +- }, +- { +- .name = "Medfield Speaker", +- .stream_name = "Speaker", +- .cpu_dai_name = "Speaker-cpu-dai", +- .codec_dai_name = "SN95031 Speaker", +- .codec_name = "sn95031", +- .platform_name = "sst-platform", +- .init = NULL, +- }, +- { +- .name = "Medfield Vibra", +- .stream_name = "Vibra1", +- .cpu_dai_name = "Vibra1-cpu-dai", +- .codec_dai_name = "SN95031 Vibra1", +- .codec_name = "sn95031", +- .platform_name = "sst-platform", +- .init = NULL, +- }, +- { +- .name = "Medfield Haptics", +- .stream_name = "Vibra2", +- .cpu_dai_name = "Vibra2-cpu-dai", +- .codec_dai_name = "SN95031 Vibra2", +- .codec_name = "sn95031", +- .platform_name = "sst-platform", +- .init = NULL, +- }, +- { +- .name = "Medfield Compress", +- .stream_name = "Speaker", +- .cpu_dai_name = "Compress-cpu-dai", +- .codec_dai_name = "SN95031 Speaker", +- .codec_name = "sn95031", +- .platform_name = "sst-platform", +- .init = NULL, +- }, +-}; +- +-/* SoC card */ +-static struct snd_soc_card snd_soc_card_mfld = { +- .name = "medfield_audio", +- .owner = THIS_MODULE, +- .dai_link = mfld_msic_dailink, +- .num_links = ARRAY_SIZE(mfld_msic_dailink), +-}; +- +-static irqreturn_t snd_mfld_jack_intr_handler(int irq, void *dev) +-{ +- struct mfld_mc_private *mc_private = (struct mfld_mc_private *) dev; +- +- memcpy_fromio(&mc_private->interrupt_status, +- ((void *)(mc_private->int_base)), +- sizeof(u8)); +- return IRQ_WAKE_THREAD; +-} +- +-static irqreturn_t snd_mfld_jack_detection(int irq, void *data) +-{ +- struct mfld_mc_private *mc_drv_ctx = (struct mfld_mc_private *) data; +- +- if (mfld_jack.codec == NULL) +- return IRQ_HANDLED; +- mfld_jack_check(mc_drv_ctx->interrupt_status); +- +- return IRQ_HANDLED; +-} +- +-static int snd_mfld_mc_probe(struct platform_device *pdev) +-{ +- int ret_val = 0, irq; +- struct mfld_mc_private *mc_drv_ctx; +- struct resource *irq_mem; +- +- pr_debug("snd_mfld_mc_probe called\n"); +- +- /* retrive the irq number */ +- irq = platform_get_irq(pdev, 0); +- +- /* audio interrupt base of SRAM location where +- * interrupts are stored by System FW */ +- mc_drv_ctx = kzalloc(sizeof(*mc_drv_ctx), GFP_ATOMIC); +- if (!mc_drv_ctx) { +- pr_err("allocation failed\n"); +- return -ENOMEM; +- } +- +- irq_mem = platform_get_resource_byname( +- pdev, IORESOURCE_MEM, "IRQ_BASE"); +- if (!irq_mem) { +- pr_err("no mem resource given\n"); +- ret_val = -ENODEV; +- goto unalloc; +- } +- mc_drv_ctx->int_base = ioremap_nocache(irq_mem->start, +- resource_size(irq_mem)); +- if (!mc_drv_ctx->int_base) { +- pr_err("Mapping of cache failed\n"); +- ret_val = -ENOMEM; +- goto unalloc; +- } +- /* register for interrupt */ +- ret_val = request_threaded_irq(irq, snd_mfld_jack_intr_handler, +- snd_mfld_jack_detection, +- IRQF_SHARED, pdev->dev.driver->name, mc_drv_ctx); +- if (ret_val) { +- pr_err("cannot register IRQ\n"); +- goto unalloc; +- } +- /* register the soc card */ +- snd_soc_card_mfld.dev = &pdev->dev; +- ret_val = snd_soc_register_card(&snd_soc_card_mfld); +- if (ret_val) { +- pr_debug("snd_soc_register_card failed %d\n", ret_val); +- goto freeirq; +- } +- platform_set_drvdata(pdev, mc_drv_ctx); +- pr_debug("successfully exited probe\n"); +- return ret_val; +- +-freeirq: +- free_irq(irq, mc_drv_ctx); +-unalloc: +- kfree(mc_drv_ctx); +- return ret_val; +-} +- +-static int snd_mfld_mc_remove(struct platform_device *pdev) +-{ +- struct mfld_mc_private *mc_drv_ctx = platform_get_drvdata(pdev); +- +- pr_debug("snd_mfld_mc_remove called\n"); +- free_irq(platform_get_irq(pdev, 0), mc_drv_ctx); +- snd_soc_unregister_card(&snd_soc_card_mfld); +- kfree(mc_drv_ctx); +- platform_set_drvdata(pdev, NULL); +- return 0; +-} +- +-static struct platform_driver snd_mfld_mc_driver = { +- .driver = { +- .owner = THIS_MODULE, +- .name = "msic_audio", +- }, +- .probe = snd_mfld_mc_probe, +- .remove = snd_mfld_mc_remove, +-}; +- +-module_platform_driver(snd_mfld_mc_driver); +- +-MODULE_DESCRIPTION("ASoC Intel(R) MID Machine driver"); +-MODULE_AUTHOR("Vinod Koul "); +-MODULE_AUTHOR("Harsha Priya "); +-MODULE_LICENSE("GPL v2"); +-MODULE_ALIAS("platform:msic-audio"); +diff --git a/sound/soc/mid-x86/sst_dsp.h b/sound/soc/mid-x86/sst_dsp.h +deleted file mode 100644 +index 0fce1de..0000000 +--- a/sound/soc/mid-x86/sst_dsp.h ++++ /dev/null +@@ -1,134 +0,0 @@ +-#ifndef __SST_DSP_H__ +-#define __SST_DSP_H__ +-/* +- * sst_dsp.h - Intel SST Driver for audio engine +- * +- * Copyright (C) 2008-12 Intel Corporation +- * Authors: Vinod Koul +- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +- * +- * 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. +- * +- * 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. +- * +- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +- */ +- +-enum sst_codec_types { +- /* AUDIO/MUSIC CODEC Type Definitions */ +- SST_CODEC_TYPE_UNKNOWN = 0, +- SST_CODEC_TYPE_PCM, /* Pass through Audio codec */ +- SST_CODEC_TYPE_MP3, +- SST_CODEC_TYPE_MP24, +- SST_CODEC_TYPE_AAC, +- SST_CODEC_TYPE_AACP, +- SST_CODEC_TYPE_eAACP, +-}; +- +-enum stream_type { +- SST_STREAM_TYPE_NONE = 0, +- SST_STREAM_TYPE_MUSIC = 1, +-}; +- +-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 */ +- u32 sfreq; /* Sampling rate in Hz */ +- u8 use_offload_path; +- u8 reserved2; +- u16 reserved3; +- u8 channel_map[8]; +-} __packed; +- +-/* 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) */ +- u8 reserved1; /* unused*/ +- u16 reserved2; /* Unused */ +-} __packed; +- +-#define AAC_BIT_STREAM_ADTS 0 +-#define AAC_BIT_STREAM_ADIF 1 +-#define AAC_BIT_STREAM_RAW 2 +- +-/* 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 */ +- u8 bs_format; /* input bit stream format adts=0, adif=1, raw=2 */ +- u16 reser2; +- u32 externalsr; /*sampling rate of basic AAC raw bit stream*/ +- u8 sbr_signalling;/*disable/enable/set automode the SBR tool.AAC+*/ +- u8 reser1; +- u16 reser3; +-} __packed; +- +-/* 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. */ +- u32 sfreq; /* Sampling freq eg. 8000, 441000, 48000 */ +- u32 channel_mask; /* Channel Mask */ +- u16 format_tag; /* Format Tag */ +- u16 block_align; /* packet size */ +- u16 wma_encode_opt;/* Encoder option */ +- u8 op_align; /* op align 0- 16 bit, 1- MSB, 2 LSB */ +- u8 reserved; /* reserved */ +-} __packed; +- +-/* Codec params struture */ +-union snd_sst_codec_params { +- struct snd_pcm_params pcm_params; +- struct snd_mp3_params mp3_params; +- struct snd_aac_params aac_params; +- struct snd_wma_params wma_params; +-} __packed; +- +-/* Address and size info of a frame buffer */ +-struct sst_address_info { +- u32 addr; /* Address at IA */ +- u32 size; /* Size of the buffer */ +-}; +- +-struct snd_sst_alloc_params_ext { +- struct sst_address_info ring_buf_info[8]; +- u8 sg_count; +- u8 reserved; +- u16 reserved2; +- u32 frag_size; /*Number of samples after which period elapsed +- message is sent valid only if path = 0*/ +-} __packed; +- +-struct snd_sst_stream_params { +- union snd_sst_codec_params uc; +-} __packed; +- +-struct snd_sst_params { +- u32 stream_id; +- u8 codec; +- u8 ops; +- u8 stream_type; +- u8 device_type; +- struct snd_sst_stream_params sparams; +- struct snd_sst_alloc_params_ext aparams; +-}; +- +-#endif /* __SST_DSP_H__ */ +diff --git a/sound/soc/mid-x86/sst_platform.c b/sound/soc/mid-x86/sst_platform.c +deleted file mode 100644 +index 392fc0b..0000000 +--- a/sound/soc/mid-x86/sst_platform.c ++++ /dev/null +@@ -1,733 +0,0 @@ +-/* +- * sst_platform.c - Intel MID Platform driver +- * +- * Copyright (C) 2010-2013 Intel Corp +- * Author: Vinod Koul +- * Author: Harsha Priya +- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +- * +- * 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. +- * +- * 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 +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include "sst_platform.h" +- +-static struct sst_device *sst; +-static DEFINE_MUTEX(sst_lock); +- +-int sst_register_dsp(struct sst_device *dev) +-{ +- BUG_ON(!dev); +- if (!try_module_get(dev->dev->driver->owner)) +- return -ENODEV; +- mutex_lock(&sst_lock); +- if (sst) { +- pr_err("we already have a device %s\n", sst->name); +- module_put(dev->dev->driver->owner); +- mutex_unlock(&sst_lock); +- return -EEXIST; +- } +- pr_debug("registering device %s\n", dev->name); +- sst = dev; +- mutex_unlock(&sst_lock); +- return 0; +-} +-EXPORT_SYMBOL_GPL(sst_register_dsp); +- +-int sst_unregister_dsp(struct sst_device *dev) +-{ +- BUG_ON(!dev); +- if (dev != sst) +- return -EINVAL; +- +- mutex_lock(&sst_lock); +- +- if (!sst) { +- mutex_unlock(&sst_lock); +- return -EIO; +- } +- +- module_put(sst->dev->driver->owner); +- pr_debug("unreg %s\n", sst->name); +- sst = NULL; +- mutex_unlock(&sst_lock); +- return 0; +-} +-EXPORT_SYMBOL_GPL(sst_unregister_dsp); +- +-static struct snd_pcm_hardware sst_platform_pcm_hw = { +- .info = (SNDRV_PCM_INFO_INTERLEAVED | +- SNDRV_PCM_INFO_DOUBLE | +- SNDRV_PCM_INFO_PAUSE | +- SNDRV_PCM_INFO_RESUME | +- SNDRV_PCM_INFO_MMAP| +- SNDRV_PCM_INFO_MMAP_VALID | +- SNDRV_PCM_INFO_BLOCK_TRANSFER | +- SNDRV_PCM_INFO_SYNC_START), +- .formats = (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16 | +- SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_U24 | +- SNDRV_PCM_FMTBIT_S32 | SNDRV_PCM_FMTBIT_U32), +- .rates = (SNDRV_PCM_RATE_8000| +- SNDRV_PCM_RATE_44100 | +- SNDRV_PCM_RATE_48000), +- .rate_min = SST_MIN_RATE, +- .rate_max = SST_MAX_RATE, +- .channels_min = SST_MIN_CHANNEL, +- .channels_max = SST_MAX_CHANNEL, +- .buffer_bytes_max = SST_MAX_BUFFER, +- .period_bytes_min = SST_MIN_PERIOD_BYTES, +- .period_bytes_max = SST_MAX_PERIOD_BYTES, +- .periods_min = SST_MIN_PERIODS, +- .periods_max = SST_MAX_PERIODS, +- .fifo_size = SST_FIFO_SIZE, +-}; +- +-/* MFLD - MSIC */ +-static struct snd_soc_dai_driver sst_platform_dai[] = { +-{ +- .name = "Headset-cpu-dai", +- .id = 0, +- .playback = { +- .channels_min = SST_STEREO, +- .channels_max = SST_STEREO, +- .rates = SNDRV_PCM_RATE_48000, +- .formats = SNDRV_PCM_FMTBIT_S24_LE, +- }, +- .capture = { +- .channels_min = 1, +- .channels_max = 5, +- .rates = SNDRV_PCM_RATE_48000, +- .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, +- .playback = { +- .channels_min = SST_STEREO, +- .channels_max = SST_STEREO, +- .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000, +- .formats = SNDRV_PCM_FMTBIT_S16_LE, +- }, +-}, +-}; +- +-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, +- int state) +-{ +- unsigned long flags; +- spin_lock_irqsave(&stream->status_lock, flags); +- stream->stream_status = state; +- spin_unlock_irqrestore(&stream->status_lock, flags); +-} +- +-static inline int sst_get_stream_status(struct sst_runtime_stream *stream) +-{ +- int state; +- unsigned long flags; +- +- spin_lock_irqsave(&stream->status_lock, flags); +- state = stream->stream_status; +- spin_unlock_irqrestore(&stream->status_lock, flags); +- return state; +-} +- +-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; +- param->sfreq = substream->runtime->rate; +- param->ring_buffer_size = snd_pcm_lib_buffer_bytes(substream); +- param->period_count = substream->runtime->period_size; +- param->ring_buffer_addr = virt_to_phys(substream->dma_buffer.area); +- pr_debug("period_cnt = %d\n", param->period_count); +- pr_debug("sfreq= %d, wd_sz = %d\n", param->sfreq, param->pcm_wd_sz); +-} +- +-static int sst_platform_alloc_stream(struct snd_pcm_substream *substream) +-{ +- struct sst_runtime_stream *stream = +- substream->runtime->private_data; +- struct sst_pcm_params param = {0}; +- struct sst_stream_params str_params = {0}; +- int ret_val; +- +- /* set codec params and inform SST driver the same */ +- sst_fill_pcm_params(substream, ¶m); +- substream->runtime->dma_area = substream->dma_buffer.area; +- str_params.sparams = param; +- str_params.codec = param.codec; +- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +- str_params.ops = STREAM_OPS_PLAYBACK; +- str_params.device_type = substream->pcm->device + 1; +- pr_debug("Playbck stream,Device %d\n", +- substream->pcm->device); +- } else { +- str_params.ops = STREAM_OPS_CAPTURE; +- str_params.device_type = SND_SST_DEVICE_CAPTURE; +- pr_debug("Capture stream,Device %d\n", +- substream->pcm->device); +- } +- ret_val = stream->ops->open(&str_params); +- pr_debug("SST_SND_PLAY/CAPTURE ret_val = %x\n", ret_val); +- if (ret_val < 0) +- return ret_val; +- +- stream->stream_info.str_id = ret_val; +- pr_debug("str id : %d\n", stream->stream_info.str_id); +- return ret_val; +-} +- +-static void sst_period_elapsed(void *mad_substream) +-{ +- struct snd_pcm_substream *substream = mad_substream; +- struct sst_runtime_stream *stream; +- int status; +- +- if (!substream || !substream->runtime) +- return; +- stream = substream->runtime->private_data; +- if (!stream) +- return; +- status = sst_get_stream_status(stream); +- if (status != SST_PLATFORM_RUNNING) +- return; +- snd_pcm_period_elapsed(substream); +-} +- +-static int sst_platform_init_stream(struct snd_pcm_substream *substream) +-{ +- struct sst_runtime_stream *stream = +- substream->runtime->private_data; +- int ret_val; +- +- pr_debug("setting buffer ptr param\n"); +- sst_set_stream_status(stream, SST_PLATFORM_INIT); +- stream->stream_info.period_elapsed = sst_period_elapsed; +- stream->stream_info.mad_substream = substream; +- stream->stream_info.buffer_ptr = 0; +- stream->stream_info.sfreq = substream->runtime->rate; +- ret_val = stream->ops->device_control( +- SST_SND_STREAM_INIT, &stream->stream_info); +- if (ret_val) +- pr_err("control_set ret error %d\n", ret_val); +- return ret_val; +- +-} +-/* end -- helper functions */ +- +-static int sst_platform_open(struct snd_pcm_substream *substream) +-{ +- struct snd_pcm_runtime *runtime = substream->runtime; +- struct sst_runtime_stream *stream; +- int ret_val; +- +- pr_debug("sst_platform_open called\n"); +- +- snd_soc_set_runtime_hwparams(substream, &sst_platform_pcm_hw); +- ret_val = snd_pcm_hw_constraint_integer(runtime, +- SNDRV_PCM_HW_PARAM_PERIODS); +- if (ret_val < 0) +- return ret_val; +- +- stream = kzalloc(sizeof(*stream), GFP_KERNEL); +- if (!stream) +- return -ENOMEM; +- spin_lock_init(&stream->status_lock); +- +- /* get the sst ops */ +- mutex_lock(&sst_lock); +- if (!sst) { +- pr_err("no device available to run\n"); +- mutex_unlock(&sst_lock); +- kfree(stream); +- return -ENODEV; +- } +- if (!try_module_get(sst->dev->driver->owner)) { +- mutex_unlock(&sst_lock); +- kfree(stream); +- return -ENODEV; +- } +- stream->ops = sst->ops; +- mutex_unlock(&sst_lock); +- +- stream->stream_info.str_id = 0; +- sst_set_stream_status(stream, SST_PLATFORM_INIT); +- stream->stream_info.mad_substream = substream; +- /* allocate memory for SST API set */ +- runtime->private_data = stream; +- +- return 0; +-} +- +-static int sst_platform_close(struct snd_pcm_substream *substream) +-{ +- struct sst_runtime_stream *stream; +- int ret_val = 0, str_id; +- +- pr_debug("sst_platform_close called\n"); +- stream = substream->runtime->private_data; +- str_id = stream->stream_info.str_id; +- if (str_id) +- ret_val = stream->ops->close(str_id); +- module_put(sst->dev->driver->owner); +- kfree(stream); +- return ret_val; +-} +- +-static int sst_platform_pcm_prepare(struct snd_pcm_substream *substream) +-{ +- struct sst_runtime_stream *stream; +- int ret_val = 0, str_id; +- +- pr_debug("sst_platform_pcm_prepare called\n"); +- stream = substream->runtime->private_data; +- str_id = stream->stream_info.str_id; +- if (stream->stream_info.str_id) { +- ret_val = stream->ops->device_control( +- SST_SND_DROP, &str_id); +- return ret_val; +- } +- +- ret_val = sst_platform_alloc_stream(substream); +- if (ret_val < 0) +- return ret_val; +- snprintf(substream->pcm->id, sizeof(substream->pcm->id), +- "%d", stream->stream_info.str_id); +- +- ret_val = sst_platform_init_stream(substream); +- if (ret_val) +- return ret_val; +- substream->runtime->hw.info = SNDRV_PCM_INFO_BLOCK_TRANSFER; +- return ret_val; +-} +- +-static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream, +- int cmd) +-{ +- int ret_val = 0, str_id; +- struct sst_runtime_stream *stream; +- int str_cmd, status; +- +- pr_debug("sst_platform_pcm_trigger called\n"); +- stream = substream->runtime->private_data; +- str_id = stream->stream_info.str_id; +- switch (cmd) { +- case SNDRV_PCM_TRIGGER_START: +- pr_debug("sst: Trigger Start\n"); +- str_cmd = SST_SND_START; +- status = SST_PLATFORM_RUNNING; +- stream->stream_info.mad_substream = substream; +- break; +- case SNDRV_PCM_TRIGGER_STOP: +- pr_debug("sst: in stop\n"); +- str_cmd = SST_SND_DROP; +- status = SST_PLATFORM_DROPPED; +- break; +- case SNDRV_PCM_TRIGGER_PAUSE_PUSH: +- pr_debug("sst: in pause\n"); +- str_cmd = SST_SND_PAUSE; +- status = SST_PLATFORM_PAUSED; +- break; +- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: +- pr_debug("sst: in pause release\n"); +- str_cmd = SST_SND_RESUME; +- status = SST_PLATFORM_RUNNING; +- break; +- default: +- return -EINVAL; +- } +- ret_val = stream->ops->device_control(str_cmd, &str_id); +- if (!ret_val) +- sst_set_stream_status(stream, status); +- +- return ret_val; +-} +- +- +-static snd_pcm_uframes_t sst_platform_pcm_pointer +- (struct snd_pcm_substream *substream) +-{ +- struct sst_runtime_stream *stream; +- int ret_val, status; +- struct pcm_stream_info *str_info; +- +- stream = substream->runtime->private_data; +- status = sst_get_stream_status(stream); +- if (status == SST_PLATFORM_INIT) +- return 0; +- str_info = &stream->stream_info; +- ret_val = stream->ops->device_control( +- SST_SND_BUFFER_POINTER, str_info); +- if (ret_val) { +- pr_err("sst: error code = %d\n", ret_val); +- return ret_val; +- } +- return stream->stream_info.buffer_ptr; +-} +- +-static int sst_platform_pcm_hw_params(struct snd_pcm_substream *substream, +- struct snd_pcm_hw_params *params) +-{ +- snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); +- memset(substream->runtime->dma_area, 0, params_buffer_bytes(params)); +- +- return 0; +-} +- +-static int sst_platform_pcm_hw_free(struct snd_pcm_substream *substream) +-{ +- return snd_pcm_lib_free_pages(substream); +-} +- +-static struct snd_pcm_ops sst_platform_ops = { +- .open = sst_platform_open, +- .close = sst_platform_close, +- .ioctl = snd_pcm_lib_ioctl, +- .prepare = sst_platform_pcm_prepare, +- .trigger = sst_platform_pcm_trigger, +- .pointer = sst_platform_pcm_pointer, +- .hw_params = sst_platform_pcm_hw_params, +- .hw_free = sst_platform_pcm_hw_free, +-}; +- +-static void sst_pcm_free(struct snd_pcm *pcm) +-{ +- pr_debug("sst_pcm_free called\n"); +- snd_pcm_lib_preallocate_free_for_all(pcm); +-} +- +-static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd) +-{ +- struct snd_pcm *pcm = rtd->pcm; +- int retval = 0; +- +- pr_debug("sst_pcm_new called\n"); +- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream || +- pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { +- retval = snd_pcm_lib_preallocate_pages_for_all(pcm, +- SNDRV_DMA_TYPE_CONTINUOUS, +- snd_dma_continuous_data(GFP_KERNEL), +- SST_MIN_BUFFER, SST_MAX_BUFFER); +- if (retval) { +- pr_err("dma buffer allocationf fail\n"); +- return retval; +- } +- } +- 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, +- .pcm_new = sst_pcm_new, +- .pcm_free = sst_pcm_free, +-}; +- +-static int sst_platform_probe(struct platform_device *pdev) +-{ +- int ret; +- +- pr_debug("sst_platform_probe called\n"); +- sst = NULL; +- ret = snd_soc_register_platform(&pdev->dev, &sst_soc_platform_drv); +- if (ret) { +- pr_err("registering soc platform failed\n"); +- return ret; +- } +- +- ret = snd_soc_register_component(&pdev->dev, &sst_component, +- sst_platform_dai, ARRAY_SIZE(sst_platform_dai)); +- if (ret) { +- pr_err("registering cpu dais failed\n"); +- snd_soc_unregister_platform(&pdev->dev); +- } +- return ret; +-} +- +-static int sst_platform_remove(struct platform_device *pdev) +-{ +- +- snd_soc_unregister_component(&pdev->dev); +- snd_soc_unregister_platform(&pdev->dev); +- pr_debug("sst_platform_remove success\n"); +- return 0; +-} +- +-static struct platform_driver sst_platform_driver = { +- .driver = { +- .name = "sst-platform", +- .owner = THIS_MODULE, +- }, +- .probe = sst_platform_probe, +- .remove = sst_platform_remove, +-}; +- +-module_platform_driver(sst_platform_driver); +- +-MODULE_DESCRIPTION("ASoC Intel(R) MID Platform driver"); +-MODULE_AUTHOR("Vinod Koul "); +-MODULE_AUTHOR("Harsha Priya "); +-MODULE_LICENSE("GPL v2"); +-MODULE_ALIAS("platform:sst-platform"); +diff --git a/sound/soc/mid-x86/sst_platform.h b/sound/soc/mid-x86/sst_platform.h +deleted file mode 100644 +index cacc906..0000000 +--- a/sound/soc/mid-x86/sst_platform.h ++++ /dev/null +@@ -1,157 +0,0 @@ +-/* +- * sst_platform.h - Intel MID Platform driver header file +- * +- * Copyright (C) 2010 Intel Corp +- * Author: Vinod Koul +- * Author: Harsha Priya +- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +- * +- * 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. +- * +- * 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__ +-#define __SST_PLATFORMDRV_H__ +- +-#include "sst_dsp.h" +- +-#define SST_MONO 1 +-#define SST_STEREO 2 +-#define SST_MAX_CAP 5 +- +-#define SST_MIN_RATE 8000 +-#define SST_MAX_RATE 48000 +-#define SST_MIN_CHANNEL 1 +-#define SST_MAX_CHANNEL 5 +-#define SST_MAX_BUFFER (800*1024) +-#define SST_MIN_BUFFER (800*1024) +-#define SST_MIN_PERIOD_BYTES 32 +-#define SST_MAX_PERIOD_BYTES SST_MAX_BUFFER +-#define SST_MIN_PERIODS 2 +-#define SST_MAX_PERIODS (1024*2) +-#define SST_FIFO_SIZE 0 +- +-struct pcm_stream_info { +- int str_id; +- void *mad_substream; +- void (*period_elapsed) (void *mad_substream); +- unsigned long long buffer_ptr; +- int sfreq; +-}; +- +-enum sst_drv_status { +- SST_PLATFORM_INIT = 1, +- SST_PLATFORM_STARTED, +- SST_PLATFORM_RUNNING, +- SST_PLATFORM_PAUSED, +- SST_PLATFORM_DROPPED, +-}; +- +-enum sst_controls { +- SST_SND_ALLOC = 0x00, +- SST_SND_PAUSE = 0x01, +- SST_SND_RESUME = 0x02, +- SST_SND_DROP = 0x03, +- SST_SND_FREE = 0x04, +- SST_SND_BUFFER_POINTER = 0x05, +- SST_SND_STREAM_INIT = 0x06, +- SST_SND_START = 0x07, +- SST_MAX_CONTROLS = 0x07, +-}; +- +-enum sst_stream_ops { +- STREAM_OPS_PLAYBACK = 0, +- STREAM_OPS_CAPTURE, +-}; +- +-enum sst_audio_device_type { +- SND_SST_DEVICE_HEADSET = 1, +- SND_SST_DEVICE_IHF, +- SND_SST_DEVICE_VIBRA, +- SND_SST_DEVICE_HAPTIC, +- SND_SST_DEVICE_CAPTURE, +- SND_SST_DEVICE_COMPRESS, +-}; +- +-/* PCM Parameters */ +-struct sst_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 */ +- u32 sfreq; /* Sampling rate in Hz */ +- u32 ring_buffer_size; +- u32 period_count; /* period elapsed in samples*/ +- u32 ring_buffer_addr; +-}; +- +-struct sst_stream_params { +- u32 result; +- u32 stream_id; +- u8 codec; +- u8 ops; +- u8 stream_type; +- u8 device_type; +- struct sst_pcm_params sparams; +-}; +- +-struct sst_compress_cb { +- void *param; +- void (*compr_cb)(void *param); +-}; +- +-struct compress_sst_ops { +- const char *name; +- int (*open) (struct snd_sst_params *str_params, +- struct sst_compress_cb *cb); +- int (*control) (unsigned int cmd, unsigned int str_id); +- int (*tstamp) (unsigned int str_id, struct snd_compr_tstamp *tstamp); +- int (*ack) (unsigned int str_id, unsigned long bytes); +- int (*close) (unsigned int str_id); +- int (*get_caps) (struct snd_compr_caps *caps); +- int (*get_codec_caps) (struct snd_compr_codec_caps *codec); +- int (*set_metadata) (unsigned int str_id, +- struct snd_compr_metadata *mdata); +- +-}; +- +-struct sst_ops { +- int (*open) (struct sst_stream_params *str_param); +- int (*device_control) (int cmd, void *arg); +- int (*close) (unsigned int str_id); +-}; +- +-struct sst_runtime_stream { +- int stream_status; +- unsigned int id; +- size_t bytes_written; +- struct pcm_stream_info stream_info; +- struct sst_ops *ops; +- struct compress_sst_ops *compr_ops; +- spinlock_t status_lock; +-}; +- +-struct sst_device { +- char *name; +- struct device *dev; +- struct sst_ops *ops; +- struct compress_sst_ops *compr_ops; +-}; +- +-int sst_register_dsp(struct sst_device *sst); +-int sst_unregister_dsp(struct sst_device *sst); +-#endif +diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c +index 06a8000..7201146 100644 +--- a/sound/soc/soc-compress.c ++++ b/sound/soc/soc-compress.c +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + + static int soc_compr_open(struct snd_compr_stream *cstream) + { +@@ -75,6 +76,98 @@ out: + return ret; + } + ++static int soc_compr_open_fe(struct snd_compr_stream *cstream) ++{ ++ struct snd_soc_pcm_runtime *fe = cstream->private_data; ++ struct snd_pcm_substream *fe_substream = fe->pcm->streams[0].substream; ++ struct snd_soc_platform *platform = fe->platform; ++ struct snd_soc_dai *cpu_dai = fe->cpu_dai; ++ struct snd_soc_dai *codec_dai = fe->codec_dai; ++ struct snd_soc_dpcm *dpcm; ++ struct snd_soc_dapm_widget_list *list; ++ int stream; ++ int ret = 0; ++ ++ if (cstream->direction == SND_COMPRESS_PLAYBACK) ++ stream = SNDRV_PCM_STREAM_PLAYBACK; ++ else ++ stream = SNDRV_PCM_STREAM_CAPTURE; ++ ++ mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); ++ ++ if (platform->driver->compr_ops && platform->driver->compr_ops->open) { ++ ret = platform->driver->compr_ops->open(cstream); ++ if (ret < 0) { ++ pr_err("compress asoc: can't open platform %s\n", platform->name); ++ goto out; ++ } ++ } ++ ++ if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->startup) { ++ ret = fe->dai_link->compr_ops->startup(cstream); ++ if (ret < 0) { ++ pr_err("compress asoc: %s startup failed\n", fe->dai_link->name); ++ goto machine_err; ++ } ++ } ++ ++ fe->dpcm[stream].runtime = fe_substream->runtime; ++ ++ if (dpcm_path_get(fe, stream, &list) <= 0) { ++ dev_dbg(fe->dev, "ASoC: %s no valid %s route\n", ++ fe->dai_link->name, stream ? "capture" : "playback"); ++ } ++ ++ /* calculate valid and active FE <-> BE dpcms */ ++ dpcm_process_paths(fe, stream, &list, 1); ++ ++ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; ++ ++ ret = dpcm_be_dai_startup(fe, stream); ++ if (ret < 0) { ++ /* clean up all links */ ++ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) ++ dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; ++ ++ dpcm_be_disconnect(fe, stream); ++ fe->dpcm[stream].runtime = NULL; ++ goto fe_err; ++ } ++ ++ dpcm_clear_pending_state(fe, stream); ++ dpcm_path_put(&list); ++ ++ fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN; ++ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; ++ ++ if (cstream->direction == SND_COMPRESS_PLAYBACK) { ++ cpu_dai->playback_active++; ++ codec_dai->playback_active++; ++ } else { ++ cpu_dai->capture_active++; ++ codec_dai->capture_active++; ++ } ++ ++ cpu_dai->active++; ++ codec_dai->active++; ++ fe->codec->active++; ++ ++ mutex_unlock(&fe->card->mutex); ++ ++ return 0; ++ ++fe_err: ++ if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->shutdown) ++ fe->dai_link->compr_ops->shutdown(cstream); ++machine_err: ++ if (platform->driver->compr_ops && platform->driver->compr_ops->free) ++ platform->driver->compr_ops->free(cstream); ++out: ++ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; ++ mutex_unlock(&fe->card->mutex); ++ return ret; ++} ++ + /* + * Power down the audio subsystem pmdown_time msecs after close is called. + * This is to ensure there are no pops or clicks in between any music tracks +@@ -88,18 +181,17 @@ static void close_delayed_work(struct work_struct *work) + + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + +- dev_dbg(rtd->dev, "ASoC: pop wq checking: %s status: %s waiting: %s\n", +- codec_dai->driver->playback.stream_name, +- codec_dai->playback_active ? "active" : "inactive", +- rtd->pop_wait ? "yes" : "no"); ++ dev_dbg (rtd->dev, "ASoC: pop wq checking: %s status: %s waiting: %s\n", ++ codec_dai->driver->playback.stream_name, ++ codec_dai->playback_active ? "active" : "inactive", ++ rtd->pop_wait ? "yes" : "no"); + + /* are we waiting on this codec DAI stream */ +- if (rtd->pop_wait == 1) { ++ if (rtd->pop_wait == 1 && !codec_dai->active) { + rtd->pop_wait = 0; + snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, + SND_SOC_DAPM_STREAM_STOP); + } +- + mutex_unlock(&rtd->pcm_mutex); + } + +@@ -120,8 +212,8 @@ static int soc_compr_free(struct snd_compr_stream *cstream) + cpu_dai->capture_active--; + codec_dai->capture_active--; + } +- +- snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction); ++ if (!codec_dai->playback_active) ++ snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction); + + cpu_dai->active--; + codec_dai->active--; +@@ -133,7 +225,6 @@ static int soc_compr_free(struct snd_compr_stream *cstream) + if (!codec_dai->active) + codec_dai->rate = 0; + +- + if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->shutdown) + rtd->dai_link->compr_ops->shutdown(cstream); + +@@ -141,7 +232,8 @@ static int soc_compr_free(struct snd_compr_stream *cstream) + platform->driver->compr_ops->free(cstream); + cpu_dai->runtime = NULL; + +- if (cstream->direction == SND_COMPRESS_PLAYBACK) { ++ if (cstream->direction == SND_COMPRESS_PLAYBACK ++ && !codec_dai->playback_active) { + if (!rtd->pmdown_time || codec->ignore_pmdown_time || + rtd->dai_link->ignore_pmdown_time) { + snd_soc_dapm_stream_event(rtd, +@@ -152,7 +244,8 @@ static int soc_compr_free(struct snd_compr_stream *cstream) + schedule_delayed_work(&rtd->delayed_work, + msecs_to_jiffies(rtd->pmdown_time)); + } +- } else { ++ } else if (cstream->direction == SND_COMPRESS_CAPTURE ++ && !codec_dai->capture_active) { + /* capture streams can be powered down now */ + snd_soc_dapm_stream_event(rtd, + SNDRV_PCM_STREAM_CAPTURE, +@@ -163,15 +256,97 @@ static int soc_compr_free(struct snd_compr_stream *cstream) + return 0; + } + ++static int soc_compr_free_fe(struct snd_compr_stream *cstream) ++{ ++ struct snd_soc_pcm_runtime *fe = cstream->private_data; ++ struct snd_soc_platform *platform = fe->platform; ++ struct snd_soc_dai *cpu_dai = fe->cpu_dai; ++ struct snd_soc_dai *codec_dai = fe->codec_dai; ++ struct snd_soc_dpcm *dpcm; ++ int stream, ret; ++ ++ mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); ++ ++ if (cstream->direction == SND_COMPRESS_PLAYBACK) { ++ stream = SNDRV_PCM_STREAM_PLAYBACK; ++ cpu_dai->playback_active--; ++ codec_dai->playback_active--; ++ } else { ++ stream = SNDRV_PCM_STREAM_CAPTURE; ++ cpu_dai->capture_active--; ++ codec_dai->capture_active--; ++ } ++ ++ cpu_dai->active--; ++ codec_dai->active--; ++ fe->codec->active--; ++ ++ snd_soc_dai_digital_mute(cpu_dai, 1, stream); ++ ++ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; ++ ++ ret = dpcm_be_dai_hw_free(fe, stream); ++ if (ret < 0) ++ dev_err(fe->dev, "compressed hw_free failed %d\n", ret); ++ ++ ret = dpcm_be_dai_shutdown(fe, stream); ++ ++ /* mark FE's links ready to prune */ ++ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) ++ dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; ++ ++ if (stream == SNDRV_PCM_STREAM_PLAYBACK) ++ dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); ++ else ++ dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); ++ ++ fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; ++ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; ++ ++ dpcm_be_disconnect(fe, stream); ++ ++ fe->dpcm[stream].runtime = NULL; ++ ++ if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->shutdown) ++ fe->dai_link->compr_ops->shutdown(cstream); ++ ++ if (platform->driver->compr_ops && platform->driver->compr_ops->free) ++ platform->driver->compr_ops->free(cstream); ++ ++ mutex_unlock(&fe->card->mutex); ++ return 0; ++} ++ + static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) + { + + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_platform *platform = rtd->platform; +- struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret = 0; + +- mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); ++ if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) { ++ ret = platform->driver->compr_ops->trigger(cstream, cmd); ++ if (ret < 0) ++ goto out; ++ } ++ ++out: ++ return ret; ++} ++ ++static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd) ++{ ++ struct snd_soc_pcm_runtime *fe = cstream->private_data; ++ struct snd_soc_platform *platform = fe->platform; ++ int ret = 0, stream; ++ ++ if (cstream->direction == SND_COMPRESS_PLAYBACK) ++ stream = SNDRV_PCM_STREAM_PLAYBACK; ++ else ++ stream = SNDRV_PCM_STREAM_CAPTURE; ++ ++ ++ mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + + if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) { + ret = platform->driver->compr_ops->trigger(cstream, cmd); +@@ -179,17 +354,28 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) + goto out; + } + ++ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; ++ ++ ret = dpcm_be_dai_trigger(fe, stream, cmd); ++ + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: +- snd_soc_dai_digital_mute(codec_dai, 0, cstream->direction); ++ case SNDRV_PCM_TRIGGER_RESUME: ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ fe->dpcm[stream].state = SND_SOC_DPCM_STATE_START; + break; + case SNDRV_PCM_TRIGGER_STOP: +- snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction); ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ fe->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; ++ break; ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED; + break; + } + + out: +- mutex_unlock(&rtd->pcm_mutex); ++ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; ++ mutex_unlock(&fe->card->mutex); + return ret; + } + +@@ -220,23 +406,88 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, + goto err; + } + +- if (cstream->direction == SND_COMPRESS_PLAYBACK) +- snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, +- SND_SOC_DAPM_STREAM_START); +- else +- snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE, ++ /* cancel any delayed stream shutdown that is pending */ ++ if (cstream->direction == SND_COMPRESS_PLAYBACK ++ && rtd->pop_wait) { ++ rtd->pop_wait = 0; ++ cancel_delayed_work(&rtd->delayed_work); ++ } ++ ++ snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, + SND_SOC_DAPM_STREAM_START); + +- /* cancel any delayed stream shutdown that is pending */ +- rtd->pop_wait = 0; ++ snd_soc_dai_digital_mute(rtd->codec_dai, 0, cstream->direction); ++err: + mutex_unlock(&rtd->pcm_mutex); ++ return ret; ++} + +- cancel_delayed_work_sync(&rtd->delayed_work); ++static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, ++ struct snd_compr_params *params) ++{ ++ struct snd_soc_pcm_runtime *fe = cstream->private_data; ++ struct snd_pcm_substream *fe_substream = fe->pcm->streams[0].substream; ++ struct snd_soc_platform *platform = fe->platform; ++ struct snd_soc_dai *cpu_dai = fe->cpu_dai; ++ struct snd_soc_dai *codec_dai = fe->codec_dai; + +- return ret; ++ struct snd_pcm_hw_params *hw_params; ++ int ret = 0, stream; + +-err: +- mutex_unlock(&rtd->pcm_mutex); ++ if (cstream->direction == SND_COMPRESS_PLAYBACK) ++ stream = SNDRV_PCM_STREAM_PLAYBACK; ++ else ++ stream = SNDRV_PCM_STREAM_CAPTURE; ++ ++ hw_params = kzalloc(sizeof(*hw_params), GFP_KERNEL); ++ if (hw_params == NULL) ++ return -ENOMEM; ++ ++ mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); ++ ++ /* first we call set_params for the platform driver ++ * this should configure the soc side ++ * if the machine has compressed ops then we call that as well ++ * expectation is that platform and machine will configure everything ++ * for this compress path, like configuring pcm port for codec ++ */ ++ if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) { ++ ret = platform->driver->compr_ops->set_params(cstream, params); ++ if (ret < 0) ++ goto out; ++ } ++ ++ if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->set_params) { ++ ret = fe->dai_link->compr_ops->set_params(cstream); ++ if (ret < 0) ++ goto out; ++ } ++ ++ memcpy(&fe->dpcm[fe_substream->stream].hw_params, params, ++ sizeof(struct snd_pcm_hw_params)); ++ ++ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; ++ ++ ret = dpcm_be_dai_hw_params(fe, stream); ++ if (ret < 0) ++ goto out; ++ ++ ret = dpcm_be_dai_prepare(fe, stream); ++ if (ret < 0) ++ goto out; ++ ++ if (stream == SNDRV_PCM_STREAM_PLAYBACK) ++ dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); ++ else ++ dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); ++ ++ fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; ++ ++ snd_soc_dai_digital_mute(cpu_dai, 0, stream); ++ ++out: ++ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; ++ mutex_unlock(&fe->card->mutex); + return ret; + } + +@@ -359,6 +610,7 @@ static int sst_compr_get_metadata(struct snd_compr_stream *cstream, + + return ret; + } ++ + /* ASoC Compress operations */ + static struct snd_compr_ops soc_compr_ops = { + .open = soc_compr_open, +@@ -374,6 +626,21 @@ static struct snd_compr_ops soc_compr_ops = { + .get_codec_caps = soc_compr_get_codec_caps + }; + ++/* ASoC Dynamic Compress operations */ ++static struct snd_compr_ops soc_compr_dyn_ops = { ++ .open = soc_compr_open_fe, ++ .free = soc_compr_free_fe, ++ .set_params = soc_compr_set_params_fe, ++ .get_params = soc_compr_get_params, ++ .set_metadata = sst_compr_set_metadata, ++ .get_metadata = sst_compr_get_metadata, ++ .trigger = soc_compr_trigger_fe, ++ .pointer = soc_compr_pointer, ++ .ack = soc_compr_ack, ++ .get_caps = soc_compr_get_caps, ++ .get_codec_caps = soc_compr_get_codec_caps ++}; ++ + /* create a new compress */ + int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) + { +@@ -382,6 +649,7 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_compr *compr; ++ struct snd_pcm *be_pcm; + char new_name[64]; + int ret = 0, direction = 0; + +@@ -409,7 +677,26 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) + ret = -ENOMEM; + goto compr_err; + } +- memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops)); ++ ++ if (rtd->dai_link->dynamic) { ++ snprintf(new_name, sizeof(new_name), "(%s)", ++ rtd->dai_link->stream_name); ++ ++ ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num, ++ 1, 0, &be_pcm); ++ if (ret < 0) { ++ dev_err(rtd->card->dev, "ASoC: can't create compressed for %s\n", ++ rtd->dai_link->name); ++ goto compr_err; ++ } ++ ++ rtd->pcm = be_pcm; ++ rtd->fe_compr = 1; ++ be_pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd; ++ /*be_pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd;*/ ++ memcpy(compr->ops, &soc_compr_dyn_ops, sizeof(soc_compr_dyn_ops)); ++ } else ++ memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops)); + + /* Add copy callback for not memory mapped DSPs */ + if (platform->driver->compr_ops && platform->driver->compr_ops->copy) +diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c +index d56bbea..717e6e9 100644 +--- a/sound/soc/soc-core.c ++++ b/sound/soc/soc-core.c +@@ -530,6 +530,15 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec) + } + #endif + ++static void codec2codec_close_delayed_work(struct work_struct *work) ++{ ++ /* Currently nothing to do for c2c links ++ * Since c2c links are internal nodes in the DAPM graph and ++ * don't interface with the outside world or application layer ++ * we don't have to do any special handling on close. ++ */ ++} ++ + #ifdef CONFIG_PM_SLEEP + /* powers down audio subsystem for suspend */ + int snd_soc_suspend(struct device *dev) +@@ -1163,6 +1172,11 @@ static int soc_probe_platform(struct snd_soc_card *card, + if (dai->dev != platform->dev) + continue; + ++ /* dummy platform doesn't have and DAIs, don't add dummy-codec ++ * widgets here (since dev is the same) ++ */ ++ if (!strcmp(dai->name, "snd-soc-dummy-dai")) ++ continue; + snd_soc_dapm_new_dai_widgets(&platform->dapm, dai); + } + +@@ -1224,9 +1238,6 @@ static int soc_post_component_init(struct snd_soc_card *card, + } + rtd->card = card; + +- /* Make sure all DAPM widgets are instantiated */ +- snd_soc_dapm_new_widgets(&codec->dapm); +- + /* machine controls, routes and widgets are not prefixed */ + temp = codec->name_prefix; + codec->name_prefix = NULL; +@@ -1362,7 +1373,6 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) + return -ENODEV; + + list_add(&cpu_dai->dapm.list, &card->dapm_list); +- snd_soc_dapm_new_dai_widgets(&cpu_dai->dapm, cpu_dai); + } + + if (cpu_dai->driver->probe) { +@@ -1429,6 +1439,9 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) + return ret; + } + } else { ++ INIT_DELAYED_WORK(&rtd->delayed_work, ++ codec2codec_close_delayed_work); ++ + /* link the DAI widgets */ + play_w = codec_dai->playback_widget; + capture_w = cpu_dai->capture_widget; +@@ -1717,8 +1730,6 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) + snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes, + card->num_dapm_routes); + +- snd_soc_dapm_new_widgets(&card->dapm); +- + for (i = 0; i < card->num_links; i++) { + dai_link = &card->dai_link[i]; + dai_fmt = dai_link->dai_fmt; +@@ -3174,6 +3185,19 @@ out: + } + EXPORT_SYMBOL_GPL(snd_soc_bytes_put); + ++int snd_soc_info_bytes_ext(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *ucontrol) ++{ ++ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); ++ 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_info_bytes_ext); ++ + /** + * snd_soc_info_xr_sx - signed multi register info callback + * @kcontrol: mreg control +diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c +index 3606383..86891a3 100644 +--- a/sound/soc/soc-dapm.c ++++ b/sound/soc/soc-dapm.c +@@ -172,6 +172,17 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( + return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL); + } + ++/** ++ * snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol ++ * @kcontrol: The kcontrol ++ */ ++struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol) ++{ ++ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); ++ return wlist->widgets[0]->codec; ++} ++EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_codec); ++ + /* get snd_card from DAPM context */ + static inline struct snd_card *dapm_get_snd_card( + struct snd_soc_dapm_context *dapm) +@@ -3440,7 +3451,7 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) + break; + } + +- if (!w->sname) ++ if (!w->sname || !strstr(w->sname, dai_w->name)) + continue; + + if (dai->driver->playback.stream_name && +diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c +index 0bb5ccc..9fd4ed8 100644 +--- a/sound/soc/soc-jack.c ++++ b/sound/soc/soc-jack.c +@@ -65,6 +65,7 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) + struct snd_soc_codec *codec; + struct snd_soc_dapm_context *dapm; + struct snd_soc_jack_pin *pin; ++ unsigned int sync = 0; + int enable; + + trace_snd_soc_jack_report(jack, mask, status); +@@ -92,12 +93,16 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) + snd_soc_dapm_enable_pin(dapm, pin->pin); + else + snd_soc_dapm_disable_pin(dapm, pin->pin); ++ ++ /* we need to sync for this case only */ ++ sync = 1; + } + + /* Report before the DAPM sync to help users updating micbias status */ + blocking_notifier_call_chain(&jack->notifier, jack->status, jack); + +- snd_soc_dapm_sync(dapm); ++ if (sync) ++ snd_soc_dapm_sync(dapm); + + snd_jack_report(jack->jack, jack->status); + +@@ -318,10 +323,13 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, + INIT_DELAYED_WORK(&gpios[i].work, gpio_work); + gpios[i].jack = jack; + ++ if (!gpios[i].irq_flags) ++ gpios[i].irq_flags = ++ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; ++ + ret = request_any_context_irq(gpio_to_irq(gpios[i].gpio), + gpio_handler, +- IRQF_TRIGGER_RISING | +- IRQF_TRIGGER_FALLING, ++ gpios[i].irq_flags, + gpios[i].name, + &gpios[i]); + if (ret < 0) +diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c +index ccb6be4..fc27d27 100644 +--- a/sound/soc/soc-pcm.c ++++ b/sound/soc/soc-pcm.c +@@ -34,7 +34,7 @@ + #define DPCM_MAX_BE_USERS 8 + + /* DPCM stream event, send event to FE and all active BEs. */ +-static int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, ++int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, + int event) + { + struct snd_soc_dpcm *dpcm; +@@ -337,7 +337,7 @@ static void close_delayed_work(struct work_struct *work) + rtd->pop_wait ? "yes" : "no"); + + /* are we waiting on this codec DAI stream */ +- if (rtd->pop_wait == 1) { ++ if (rtd->pop_wait == 1 && !codec_dai->active) { + rtd->pop_wait = 0; + snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, + SND_SOC_DAPM_STREAM_STOP); +@@ -383,7 +383,10 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) + /* Muting the DAC suppresses artifacts caused during digital + * shutdown, for example from stopping clocks. + */ +- snd_soc_dai_digital_mute(codec_dai, 1, substream->stream); ++ if (!codec_dai->playback_active) ++ snd_soc_dai_digital_mute(codec_dai, 1, substream->stream); ++ ++ snd_soc_dai_digital_mute(cpu_dai, 1, substream->stream); + + if (cpu_dai->driver->ops->shutdown) + cpu_dai->driver->ops->shutdown(substream, cpu_dai); +@@ -398,7 +401,8 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) + platform->driver->ops->close(substream); + cpu_dai->runtime = NULL; + +- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ++ && !codec_dai->playback_active) { + if (!rtd->pmdown_time || codec->ignore_pmdown_time || + rtd->dai_link->ignore_pmdown_time) { + /* powered down playback stream now */ +@@ -411,14 +415,14 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) + schedule_delayed_work(&rtd->delayed_work, + msecs_to_jiffies(rtd->pmdown_time)); + } +- } else { ++ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE ++ && !codec_dai->capture_active) { + /* capture streams can be powered down now */ + snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE, + SND_SOC_DAPM_STREAM_STOP); + } + + mutex_unlock(&rtd->pcm_mutex); +- + pm_runtime_put(platform->dev); + pm_runtime_put(codec_dai->dev); + pm_runtime_put(cpu_dai->dev); +@@ -488,6 +492,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) + SND_SOC_DAPM_STREAM_START); + + snd_soc_dai_digital_mute(codec_dai, 0, substream->stream); ++ snd_soc_dai_digital_mute(cpu_dai, 0, substream->stream); + + out: + mutex_unlock(&rtd->pcm_mutex); +@@ -757,7 +762,7 @@ static void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe, + } + + /* disconnect a BE and FE */ +-static void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream) ++void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream) + { + struct snd_soc_dpcm *dpcm, *d; + +@@ -853,7 +858,7 @@ static int widget_in_list(struct snd_soc_dapm_widget_list *list, + return 0; + } + +-static int dpcm_path_get(struct snd_soc_pcm_runtime *fe, ++int dpcm_path_get(struct snd_soc_pcm_runtime *fe, + int stream, struct snd_soc_dapm_widget_list **list_) + { + struct snd_soc_dai *cpu_dai = fe->cpu_dai; +@@ -875,11 +880,6 @@ static int dpcm_path_get(struct snd_soc_pcm_runtime *fe, + return paths; + } + +-static inline void dpcm_path_put(struct snd_soc_dapm_widget_list **list) +-{ +- kfree(*list); +-} +- + static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream, + struct snd_soc_dapm_widget_list **list_) + { +@@ -949,7 +949,7 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, + continue; + + /* don't connect if FE is not running */ +- if (!fe->dpcm[stream].runtime) ++ if (!fe->dpcm[stream].runtime && !fe->fe_compr) + continue; + + /* newly connected FE and BE */ +@@ -974,7 +974,7 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, + * Find the corresponding BE DAIs that source or sink audio to this + * FE substream. + */ +-static int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, ++int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, + int stream, struct snd_soc_dapm_widget_list **list, int new) + { + if (new) +@@ -983,7 +983,7 @@ static int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, + return dpcm_prune_paths(fe, stream, list); + } + +-static void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream) ++void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream) + { + struct snd_soc_dpcm *dpcm; + +@@ -1021,7 +1021,7 @@ static void dpcm_be_dai_startup_unwind(struct snd_soc_pcm_runtime *fe, + } + } + +-static int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) ++int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) + { + struct snd_soc_dpcm *dpcm; + int err, count = 0; +@@ -1149,7 +1149,6 @@ static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream) + } + + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN; +- + dpcm_set_fe_runtime(fe_substream); + snd_pcm_limit_hw_rates(runtime); + +@@ -1163,7 +1162,7 @@ be_err: + return ret; + } + +-static int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream) ++int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream) + { + struct snd_soc_dpcm *dpcm; + +@@ -1224,7 +1223,7 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream) + return 0; + } + +-static int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) ++int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) + { + struct snd_soc_dpcm *dpcm; + +@@ -1289,7 +1288,7 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream) + return 0; + } + +-static int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) ++int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) + { + struct snd_soc_dpcm *dpcm; + int ret; +@@ -1395,7 +1394,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream, + if (ret < 0) { + dev_err(fe->dev,"ASoC: hw_params FE failed %d\n", ret); + dpcm_be_dai_hw_free(fe, stream); +- } else ++ } else + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS; + + out: +@@ -1419,7 +1418,7 @@ static int dpcm_do_trigger(struct snd_soc_dpcm *dpcm, + return ret; + } + +-static int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, ++int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, + int cmd) + { + struct snd_soc_dpcm *dpcm; +@@ -1511,7 +1510,6 @@ static int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, + + return ret; + } +-EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger); + + static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd) + { +@@ -1587,7 +1585,7 @@ out: + return ret; + } + +-static int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) ++int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) + { + struct snd_soc_dpcm *dpcm; + int ret = 0; +@@ -2011,17 +2009,36 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) + int ret = 0, playback = 0, capture = 0; + + if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) { +- if (cpu_dai->driver->playback.channels_min) +- playback = 1; +- if (cpu_dai->driver->capture.channels_min) +- capture = 1; ++ if (cpu_dai->driver->playback.channels_min) { ++ if (rtd->dai_link->playback_count) ++ playback = rtd->dai_link->playback_count; ++ else ++ playback = 1; ++ } ++ ++ if (cpu_dai->driver->capture.channels_min) { ++ if (rtd->dai_link->capture_count) ++ capture = rtd->dai_link->capture_count; ++ else ++ capture = 1; ++ } + } else { + if (codec_dai->driver->playback.channels_min && +- cpu_dai->driver->playback.channels_min) +- playback = 1; ++ cpu_dai->driver->playback.channels_min) { ++ if (rtd->dai_link->playback_count) ++ playback = rtd->dai_link->playback_count; ++ else ++ playback = 1; ++ } ++ + if (codec_dai->driver->capture.channels_min && +- cpu_dai->driver->capture.channels_min) +- capture = 1; ++ cpu_dai->driver->capture.channels_min) { ++ if (rtd->dai_link->capture_count) ++ capture = rtd->dai_link->capture_count; ++ else ++ capture = 1; ++ } ++ + } + + /* create the PCM */ +diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c +index 4b3be6c..8ecf448 100644 +--- a/sound/soc/soc-utils.c ++++ b/sound/soc/soc-utils.c +@@ -75,7 +75,11 @@ static const struct snd_pcm_hardware dummy_dma_hardware = { + + static int dummy_dma_open(struct snd_pcm_substream *substream) + { +- snd_soc_set_runtime_hwparams(substream, &dummy_dma_hardware); ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ ++ /* BE's dont need dummy params */ ++ if (!rtd->dai_link->no_pcm) ++ snd_soc_set_runtime_hwparams(substream, &dummy_dma_hardware); + + return 0; + } +@@ -89,7 +93,28 @@ static struct snd_soc_platform_driver dummy_platform = { + .ops = &dummy_dma_ops, + }; + +-static struct snd_soc_codec_driver dummy_codec; ++static struct snd_soc_dapm_widget dapm_widgets[] = { ++ SND_SOC_DAPM_INPUT("Dummy Input"), ++ SND_SOC_DAPM_OUTPUT("Dummy Output"), ++}; ++ ++static struct snd_soc_dapm_route intercon[] = { ++ { "Dummy Output", NULL, "Dummy Playback"}, ++ { "Dummy Capture", NULL, "Dummy Input"}, ++}; ++ ++static int dummy_codec_probe(struct snd_soc_codec *codec) ++{ ++ struct snd_soc_dapm_context *dapm = &codec->dapm; ++ snd_soc_dapm_new_controls(dapm, dapm_widgets, ++ ARRAY_SIZE(dapm_widgets)); ++ snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); ++ return 0; ++} ++ ++static struct snd_soc_codec_driver dummy_codec = { ++ .probe = dummy_codec_probe, ++}; + + #define STUB_RATES SNDRV_PCM_RATE_8000_192000 + #define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ +@@ -101,17 +126,18 @@ static struct snd_soc_codec_driver dummy_codec; + SNDRV_PCM_FMTBIT_S32_LE | \ + SNDRV_PCM_FMTBIT_U32_LE | \ + SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) ++ + static struct snd_soc_dai_driver dummy_dai = { + .name = "snd-soc-dummy-dai", + .playback = { +- .stream_name = "Playback", ++ .stream_name = "Dummy Playback", + .channels_min = 1, + .channels_max = 384, + .rates = STUB_RATES, + .formats = STUB_FORMATS, + }, + .capture = { +- .stream_name = "Capture", ++ .stream_name = "Dummy Capture", + .channels_min = 1, + .channels_max = 384, + .rates = STUB_RATES, -- 1.7.9.5 diff --git a/device-software/meta-edison/recipes-kernel/linux/linux-yocto_3.10.bbappend b/device-software/meta-edison/recipes-kernel/linux/linux-yocto_3.10.bbappend index 9d1778c..422c09d 100644 --- a/device-software/meta-edison/recipes-kernel/linux/linux-yocto_3.10.bbappend +++ b/device-software/meta-edison/recipes-kernel/linux/linux-yocto_3.10.bbappend @@ -6,13 +6,3 @@ SRCREV_meta = "6ad20f049abd52b515a8e0a4664861cfd331f684" SRC_URI += "file://defconfig" SRC_URI += "file://upstream_to_edison.patch" -do_configure() { - cp "${WORKDIR}/defconfig" "${B}/.config" -} -do_kernel_configme() { - cp "${WORKDIR}/defconfig" "${B}/.config" -} -do_patch() { - cd ${S} - git am "${WORKDIR}/upstream_to_edison.patch" -} diff --git a/device-software/setup.sh b/device-software/setup.sh index d155994..9d89394 100755 --- a/device-software/setup.sh +++ b/device-software/setup.sh @@ -33,14 +33,15 @@ DISTRO = "poky-edison" USER_CLASSES ?= "buildstats image-mklibs image-prelink" PATCHRESOLVE = "noop" CONF_VERSION = "1" -EDISONREPO_TOP_DIR = "$my_dir" +EDISONREPO_TOP_DIR = "$top_repo_dir" DL_DIR ?= "$my_dl_dir" SSTATE_DIR ?= "$my_sstate_dir" BUILDNAME = "$my_build_name" LICENSE_FLAGS_WHITELIST += "commercial" COPY_LIC_MANIFEST = "1" COPY_LIC_DIRS = "1" -FILESYSTEM_PERMS_TABLES = "$my_dir/device-software/meta-edison-distro/files/fs-perms.txt" +FILESYSTEM_PERMS_TABLES = "$top_repo_dir/device-software/meta-edison-distro/files/fs-perms.txt" +$extra_package_type $extra_archiving $extra_conf EOF @@ -69,11 +70,11 @@ BBLAYERS ?= " \\ $poky_dir/meta \\ $poky_dir/meta-yocto \\ $poky_dir/meta-yocto-bsp \\ - $my_dir/device-software/meta-edison \\ - $my_dir/device-software/meta-edison-distro \\ - $my_dir/device-software/meta-edison-middleware \\ - $my_dir/device-software/meta-edison-arduino \\ - $my_dir/device-software/meta-edison-devtools \\ + $top_repo_dir/device-software/meta-edison \\ + $top_repo_dir/device-software/meta-edison-distro \\ + $top_repo_dir/device-software/meta-edison-middleware \\ + $top_repo_dir/device-software/meta-edison-arduino \\ + $top_repo_dir/device-software/meta-edison-devtools \\ $extra_layers " BBLAYERS_NON_REMOVABLE ?= " \\ @@ -112,17 +113,20 @@ function usage() echo -e "\t--sdk_host=$my_sdk_host\t\tchoose host machine on which the generated SDK and cross compiler will be run. Must be one of [$all_sdk_hosts]" echo -e "\t-l --list_sdk_hosts\t\tlist availables sdk host supported machines" echo -e "\t--create_src_archive\t\twhen set, copies sources of all deployed packages into build/tmp/deploy/sources" + echo -e "\t--deb_packages\t\twhen set, use .deb package format instead of .ipk" echo "" } main() { - my_dir=$(dirname $(dirname $(readlink -f $0))) + top_repo_dir=$(dirname $(dirname $(readlink -f $0))) + my_build_dir=$top_repo_dir my_dl_dir="\${TOPDIR}/downloads" my_sstate_dir="\${TOPDIR}/sstate-cache" my_bb_number_thread=4 my_parallel_make=4 my_build_name="Custom Edison build by $USER@$HOSTNAME "$(date +"%F %H:%M:%S %Z") all_sdk_hosts="linux32 linux64 win32 win64 macosx" + extra_package_type="" #probe my_sdk_host from uname plat=$(uname -s | tr '[:upper:]' '[:lower:]') @@ -135,7 +139,7 @@ main() { my_sdk_host="$plat$arch" my_mode="external" - if [ -d "$my_dir/linux-kernel" ]; then + if [ -d "$top_repo_dir/linux-kernel" ]; then my_mode="devenv" fi @@ -153,6 +157,9 @@ ARCHIVER_MODE[src] = \"original\" COPYLEFT_LICENSE_INCLUDE = 'GPL* LGPL*' " ;; + --deb_packages) + extra_package_type="PACKAGE_CLASSES = \"package_deb\"" + ;; --dl_dir) check_path my_dl_dir=$(readlink -f "$VALUE") @@ -173,6 +180,9 @@ COPYLEFT_LICENSE_INCLUDE = 'GPL* LGPL*' --build_name) my_build_name=$VALUE ;; + --build_dir) + my_build_dir=$VALUE + ;; -l | --list_sdk_hosts) echo $all_sdk_hosts exit @@ -195,7 +205,7 @@ COPYLEFT_LICENSE_INCLUDE = 'GPL* LGPL*' echo "We are building in devenv mode, i.e. with dependency on teamforge internal servers" echo "and yocto recipes assuming local sources for some package." echo "You can change this by passing the --mode=external option to this script." - do_append_layer + do_append_layer $top_repo_dir/device-software/meta-edison-devenv else if [ "$my_mode" = "external" ] then @@ -228,38 +238,39 @@ COPYLEFT_LICENSE_INCLUDE = 'GPL* LGPL*' ;; esac - poky_dir=$my_dir/poky + poky_dir=$my_build_dir/poky # Re-create the poky dir from archive - echo "Extracting upstream Yocto tools in the poky/ directory from archive" + echo "Extracting upstream Yocto tools in the $poky_dir directory from archive" rm -rf $poky_dir - tar -xjf $my_dir/device-software/utils/poky-daisy-11.0.0.tar.bz2 - mv poky-daisy-11.0.0 $poky_dir + tar -xjf $top_repo_dir/device-software/utils/poky-daisy-11.0.1.tar.bz2 + mv poky-daisy-11.0.1 $poky_dir # Apply patch on top of it allowing to perform build in external source directory #echo "Applying patch on it" cd $poky_dir - git apply --whitespace=nowarn $my_dir/device-software/utils/fix-gcc49-binutils.patch - git apply $my_dir/device-software/utils/0001-kernel-kernel-yocto-fix-external-src-builds-when-S-B-poky-dora.patch - git apply $my_dir/device-software/utils/gcc-Clean-up-configure_prepend-and-fix-for-mingw.patch - git apply $my_dir/device-software/utils/sdk-populate-clean-broken-links.patch - git apply $my_dir/device-software/utils/fix-sshd-varloglastlog-warning.patch - git apply --whitespace=nowarn $my_dir/device-software/utils/0001-bash-fix-CVE-2014-6271.patch - git apply --whitespace=nowarn $my_dir/device-software/utils/0002-bash-Fix-CVE-2014-7169.patch + git apply $top_repo_dir/device-software/utils/0001-kernel-kernel-yocto-fix-external-src-builds-when-S-B-poky-dora.patch + git apply $top_repo_dir/device-software/utils/sdk-populate-clean-broken-links.patch + git apply --whitespace=nowarn $top_repo_dir/device-software/utils/0001-bash-fix-CVE-2014-6271.patch + git apply --whitespace=nowarn $top_repo_dir/device-software/utils/0002-bash-Fix-CVE-2014-7169.patch + git apply $top_repo_dir/device-software/utils/0001-libarchive-avoid-dependency-on-e2fsprogs.patch + git apply --whitespace=nowarn $top_repo_dir/device-software/utils/0001-busybox-handle-syslog-related-files-properly.patch + git apply $top_repo_dir/device-software/utils/0001-openssh-avoid-screen-sessions-being-killed-on-discon.patch + git apply $top_repo_dir/device-software/utils/handle_bash_func.patch mingw_dir=$poky_dir/meta-mingw echo "Unpacking Mingw layer to poky/meta-mingw/ directory from archive" mkdir -p $mingw_dir - ( cd $mingw_dir && tar -xjf $my_dir/device-software/utils/mingw-daisy.tar.bz2) + ( cd $mingw_dir && tar -xjf $top_repo_dir/device-software/utils/mingw-daisy.tar.bz2) darwin_dir=$poky_dir/meta-darwin echo "Unpacking Darwin layer to poky/meta-darwin/ directory from archive" mkdir -p $darwin_dir - ( cd $darwin_dir && tar -xjf $my_dir/device-software/utils/darwin-daisy.tar.bz2) + ( cd $darwin_dir && tar -xjf $top_repo_dir/device-software/utils/darwin-daisy.tar.bz2) if [[ $my_sdk_host == win* ]] then - do_append_layer $my_dir/device-software/meta-mingw + do_append_layer $top_repo_dir/device-software/meta-mingw do_append_layer $mingw_dir fi @@ -269,9 +280,9 @@ COPYLEFT_LICENSE_INCLUDE = 'GPL* LGPL*' fi echo "Initializing yocto build environment" - source oe-init-build-env $my_dir/build > /dev/null + source oe-init-build-env $my_build_dir/build > /dev/null - yocto_conf_dir=$my_dir/build/conf + yocto_conf_dir=$my_build_dir/build/conf echo "Setting up yocto configuration file (in build/conf/local.conf)" do_bblayers_conf @@ -280,6 +291,7 @@ COPYLEFT_LICENSE_INCLUDE = 'GPL* LGPL*' echo "** Success **" echo "SDK will be generated for $my_sdk_host host" echo "Now run these two commands to setup and build the flashable image:" + echo "cd $my_build_dir" echo "source poky/oe-init-build-env" echo "bitbake edison-image" echo "*************" diff --git a/device-software/utils/0001-busybox-handle-syslog-related-files-properly.patch b/device-software/utils/0001-busybox-handle-syslog-related-files-properly.patch new file mode 100644 index 0000000..4ce90ef --- /dev/null +++ b/device-software/utils/0001-busybox-handle-syslog-related-files-properly.patch @@ -0,0 +1,74 @@ +From 5d6ffdc47565cb7405edabcf8cb8ba1fe3c84f31 Mon Sep 17 00:00:00 2001 +From: Chen Qi +Date: Fri, 13 Jun 2014 13:42:33 +0800 +Subject: [PATCH] busybox: handle syslog related files properly + +If CONFIG_SYSLOGD is not enabled, then the related service file should +not be installed as it will always fail at system start-up. The error +message is as following. + + [FAILED] Failed to start System Logging Service. + +The same logic applies to CONFIG_KLOGD. + +So we should first check the configuration before we install these +service files. + +[YOCTO #5302] + +(From OE-Core rev: b44e291a87539fbb8e6da1a16c56f425a417e7bd) + +Signed-off-by: Chen Qi +Signed-off-by: Saul Wold +Signed-off-by: Richard Purdie +--- + meta/recipes-core/busybox/busybox.inc | 25 ++++++++++++++----------- + 1 file changed, 14 insertions(+), 14 deletions(-) + +diff --git a/meta/recipes-core/busybox/busybox.inc b/meta/recipes-core/busybox/busybox.inc +index f6cb039..bd66e4f 100644 +--- a/meta/recipes-core/busybox/busybox.inc ++++ b/meta/recipes-core/busybox/busybox.inc +@@ -40,6 +40,3 @@ + INITSCRIPT_NAME_${PN}-udhcpd = "busybox-udhcpd" + +-SYSTEMD_PACKAGES = "${PN}-syslog" +-SYSTEMD_SERVICE_${PN}-syslog = "busybox-syslog.service" +- + CONFFILES_${PN}-syslog = "${sysconfdir}/syslog-startup.conf.${BPN}" +@@ -274,18 +274,21 @@ do_install () { + fi + + if ${@base_contains('DISTRO_FEATURES','systemd','true','false',d)}; then +- install -d ${D}${systemd_unitdir}/system +- sed 's,@base_sbindir@,${base_sbindir},g' < ${WORKDIR}/busybox-syslog.service.in \ +- > ${D}${systemd_unitdir}/system/busybox-syslog.service +- sed 's,@base_sbindir@,${base_sbindir},g' < ${WORKDIR}/busybox-klogd.service.in \ +- > ${D}${systemd_unitdir}/system/busybox-klogd.service +- +- if [ -f ${WORKDIR}/busybox-syslog.default ] ; then +- install -d ${D}${sysconfdir}/default +- install -m 0644 ${WORKDIR}/busybox-syslog.default ${D}${sysconfdir}/default/busybox-syslog ++ if grep -q "CONFIG_SYSLOGD=y" ${B}/.config; then ++ install -d ${D}${systemd_unitdir}/system ++ sed 's,@base_sbindir@,${base_sbindir},g' < ${WORKDIR}/busybox-syslog.service.in \ ++ > ${D}${systemd_unitdir}/system/busybox-syslog.service ++ if [ -f ${WORKDIR}/busybox-syslog.default ] ; then ++ install -d ${D}${sysconfdir}/default ++ install -m 0644 ${WORKDIR}/busybox-syslog.default ${D}${sysconfdir}/default/busybox-syslog ++ fi ++ ln -sf /dev/null ${D}${systemd_unitdir}/system/syslog.service ++ fi ++ if grep -q "CONFIG_KLOGD=y" ${B}/.config; then ++ install -d ${D}${systemd_unitdir}/system ++ sed 's,@base_sbindir@,${base_sbindir},g' < ${WORKDIR}/busybox-klogd.service.in \ ++ > ${D}${systemd_unitdir}/system/busybox-klogd.service + fi +- +- ln -sf /dev/null ${D}${systemd_unitdir}/system/syslog.service + fi + + # Remove the sysvinit specific configuration file for systemd systems to avoid confusion +-- +1.7.9.5 + diff --git a/device-software/utils/0001-kernel-kernel-yocto-fix-external-src-builds-when-S-B-poky-dora.patch b/device-software/utils/0001-kernel-kernel-yocto-fix-external-src-builds-when-S-B-poky-dora.patch index 09f609f..1b62161 100644 --- a/device-software/utils/0001-kernel-kernel-yocto-fix-external-src-builds-when-S-B-poky-dora.patch +++ b/device-software/utils/0001-kernel-kernel-yocto-fix-external-src-builds-when-S-B-poky-dora.patch @@ -15,6 +15,7 @@ accessing files. Signed-off-by: Khem Raj Signed-off-by: Bruce Ashfield Signed-off-by: Fabien Chereau +Signed-off-by: Simon Desfarges --- meta/classes/kernel.bbclass | 63 +++++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 28 deletions(-) @@ -63,7 +64,7 @@ index 19b159b..8914f04 100644 # + pwd="$PWD" + cd "${B}" - find . -depth -not -name "*.cmd" -not -name "*.o" -not -path "./Documentation*" -not -path "./.*" -print0 | cpio --null -pdlu $kerneldir + find . -depth -not -name "*.cmd" -not -name "*.o" -not -path "./Documentation*" -not -path "./source*" -not -path "./.*" -print0 | cpio --null -pdlu $kerneldir - cp .config $kerneldir + cp ${B}/.config $kerneldir if [ "${S}" != "${B}" ]; then diff --git a/device-software/utils/0001-libarchive-avoid-dependency-on-e2fsprogs.patch b/device-software/utils/0001-libarchive-avoid-dependency-on-e2fsprogs.patch new file mode 100644 index 0000000..5bbe1f0 --- /dev/null +++ b/device-software/utils/0001-libarchive-avoid-dependency-on-e2fsprogs.patch @@ -0,0 +1,55 @@ +From f0b9a7cf9f80be1917e45266fa201f464a28c1e5 Mon Sep 17 00:00:00 2001 +From: Paul Eggleton +Date: Thu, 5 Jun 2014 10:46:16 +0100 +Subject: [PATCH] libarchive: avoid dependency on e2fsprogs + +libarchive's configure script looks for ext2fs/ext2_fs.h in order to use +some defines for file attributes support if present (but doesn't link to +any additional libraries.) There is no configure option to disable this, +and if e2fsprogs is rebuilding between do_configure and do_compile you +can currently get a failure. Because it doesn't need anything else from +e2fsprogs, and e2fsprogs isn't currently buildable for nativesdk anyway, +copy the headers in from e2fsprogs-native which we're likely to have +built already (and add it to DEPENDS just to be sure we have.) + +Fixes [YOCTO #6268]. + +(From OE-Core rev: ad754e46ad477acfbe7543187a5c38bc333b8612) + +Signed-off-by: Paul Eggleton +Signed-off-by: Saul Wold +Signed-off-by: Richard Purdie +--- + .../libarchive/libarchive_3.1.2.bb | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/meta/recipes-extended/libarchive/libarchive_3.1.2.bb b/meta/recipes-extended/libarchive/libarchive_3.1.2.bb +index 3e6cdb4..2a2d0f9 100644 +--- a/meta/recipes-extended/libarchive/libarchive_3.1.2.bb ++++ b/meta/recipes-extended/libarchive/libarchive_3.1.2.bb +@@ -5,6 +5,8 @@ SECTION = "devel" + LICENSE = "BSD" + LIC_FILES_CHKSUM = "file://COPYING;md5=b4e3ffd607d6686c6cb2f63394370841" + ++DEPENDS = "e2fsprogs-native" ++ + PACKAGECONFIG ?= "libxml2 zlib bz2" + + PACKAGECONFIG_append_class-target = "\ +@@ -36,4 +38,13 @@ SRC_URI[sha256sum] = "eb87eacd8fe49e8d90c8fdc189813023ccc319c5e752b01fb6ad0cc7b2 + + inherit autotools-brokensep lib_package + ++CPPFLAGS += "-I${WORKDIR}/extra-includes" ++ ++do_configure[cleandirs] += "${WORKDIR}/extra-includes" ++do_configure_prepend() { ++ # We just need the headers for some type constants, so no need to ++ # build all of e2fsprogs for the target ++ cp -R ${STAGING_INCDIR_NATIVE}/ext2fs ${WORKDIR}/extra-includes/ ++} ++ + BBCLASSEXTEND = "native nativesdk" +-- +1.7.9.5 + diff --git a/device-software/utils/0001-openssh-avoid-screen-sessions-being-killed-on-discon.patch b/device-software/utils/0001-openssh-avoid-screen-sessions-being-killed-on-discon.patch new file mode 100644 index 0000000..acc7162 --- /dev/null +++ b/device-software/utils/0001-openssh-avoid-screen-sessions-being-killed-on-discon.patch @@ -0,0 +1,36 @@ +From 6c576a4ac85307a5c851bee91a567cae3ea5a43b Mon Sep 17 00:00:00 2001 +From: Paul Eggleton +Date: Wed, 5 Nov 2014 21:08:51 -0800 +Subject: [PATCH] openssh: avoid screen sessions being killed on disconnect + with systemd + +Tell systemd just to kill the sshd process when the ssh connection drops +instead of the entire cgroup for sshd, so that any screen sessions (and +more to the point, processes within them) do not get killed. + +(This is what the Fedora sshd service file does, and what we're already +doing in the dropbear service file). + +(From OE-Core master rev: 3c238dff41fbd3687457989c7b17d22b2cc844be) + +(From OE-Core rev: 6e6aeb7cca52b92a0c8013473e2b8bb18738a119) + +Signed-off-by: Paul Eggleton +Signed-off-by: Ross Burton +Signed-off-by: Richard Purdie +--- + .../openssh/openssh/sshd@.service | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/meta/recipes-connectivity/openssh/openssh/sshd@.service b/meta/recipes-connectivity/openssh/openssh/sshd@.service +index 4eda659..bb2d68e 100644 +--- a/meta/recipes-connectivity/openssh/openssh/sshd@.service ++++ b/meta/recipes-connectivity/openssh/openssh/sshd@.service +@@ -8,3 +8,4 @@ ExecStart=-@SBINDIR@/sshd -i + ExecReload=@BASE_BINDIR@/kill -HUP $MAINPID + StandardInput=socket + StandardError=syslog ++KillMode=process +-- +1.7.9.5 + diff --git a/device-software/utils/Makefile.mk b/device-software/utils/Makefile.mk new file mode 100644 index 0000000..674b962 --- /dev/null +++ b/device-software/utils/Makefile.mk @@ -0,0 +1,182 @@ +# Top level makefile, repo tool should create a link on this file at the root +# of the build environement. + +.DEFAULT_GOAL := image + +# Parallelism is managed by bitbake for this project +.NOTPARALLEL: + +# In this makefile, all targets are phony because dependencies are managed at the bitbake level. +# We don't need to specify all targets here because no files named like them exist at the top level directory. +.PHONY : bbcache + +# Use a default build tag when none is set by the caller +NOWDATE := $(shell date +"%Y%m%d%H%M%S") +BUILD_TAG ?= custom_build_$(USER)@$(HOSTNAME)$(NOWDATE) +BB_DL_DIR ?= $(CURDIR)/bbcache/downloads +BB_SSTATE_DIR ?= $(CURDIR)/bbcache/sstate-cache + +############################################################################### +# Main targets +############################################################################### +setup: _setup-sdkhost_linux64 + +cleansstate: _check_setup_was_done + /bin/bash -c "source out/current/poky/oe-init-build-env $(CURDIR)/out/current/build ; $(CURDIR)/device-software/utils/invalidate_sstate.sh $(CURDIR)/out/current/build" + +devtools_package: _check_setup_was_done + /bin/bash -c "source out/current/poky/oe-init-build-env $(CURDIR)/out/current/build ; $(CURDIR)/device-software/utils/create_devtools_package.sh $(CURDIR)/out/current/build" + +sdk: _check_setup_was_done + /bin/bash -c "source out/current/poky/oe-init-build-env $(CURDIR)/out/current/build ; bitbake edison-image -c populate_sdk" + +src-package: pub + ./device-software/utils/create_src_package.sh + mv edison-src.tgz $(CURDIR)/pub/edison-src-$(BUILD_TAG).tgz + +clean: + rm -rf out + +u-boot linux-externalsrc edison-image meta-toolchain bootimg: _check_setup_was_done + /bin/bash -c "source out/current/poky/oe-init-build-env $(CURDIR)/out/current/build ; bitbake -c cleansstate $@ ; bitbake $@" + ./device-software/utils/flash/postBuild.sh $(CURDIR)/out/current/build + +bootloader: u-boot + +image: edison-image + +kernel: linux-externalsrc bootimg + +toolchain: meta-toolchain + +flash: _check_postbuild_was_done + ./out/current/build/toFlash/flashall.sh + +flash-kernel: _check_postbuild_was_done + dd if=./out/current/build/toFlash/edison-image-edison.hddimg | ssh root@192.168.2.15 "dd of=/dev/disk/by-partlabel/boot bs=1M" + ssh root@192.168.2.15 "/sbin/reboot -f" + +flash-bootloader: _check_postbuild_was_done + dfu-util -d 8087:0a99 --alt u-boot0 -D ./out/current/build/toFlash/u-boot-edison.bin -R + +cscope: + find linux-kernel/ u-boot -regex '.*\.\(c\|cpp\|h\)$\' > cscope.files + cscope -R -b -k + +list: + @sh -c "$(MAKE) -p _no_targets | awk -F':' '/^[a-zA-Z0-9][^\$$#\/\\t=]*:([^=]|$$)/ {split(\$$1,A,/ /);for(i in A)print A[i]}' | sort" + +debian_image: + $(MAKE) setup SETUP_ARGS="$(SETUP_ARGS) --deb_packages" + $(MAKE) image + @echo '*******************************' + @echo '*******************************' + @echo 'Now run the following command to create the debian rootfs:' + @echo 'sudo $(CURDIR)/device-software/utils/create-debian-image.sh --build_dir=$(CURDIR)/out/current/build' + @echo 'and run a regular make flash' + @echo '*******************************' + +help: + @echo 'Main targets:' + @echo ' help - show this help' + @echo ' clean - remove the out and pub directory' + @echo ' setup - prepare the build env for later build operations' + @echo ' cleansstate - clean the sstate for some recipes to work-around some bitbake limitations' + @echo ' image - build the flashable edison image, results are in out/current/build/toFlash' + @echo ' flash - flash the current build image' + @echo ' sdk - build the SDK for the current build' + @echo ' toolchain - build the cross compilation toolchain for the current build' + @echo ' src-package - create the external source package' + @echo ' devtools_package - build some extra dev tools packages, results are in out/current/build/devtools_packages/' + @echo + @echo 'Continuous Integration targets:' + @sh -c "$(MAKE) -p _no_targets | awk -F':' '/^ci_[a-zA-Z0-9][^\$$#\/\\t=]*:([^=]|$$)/ {split(\$$1,A,/ /);for(i in A)print \" \"A[i]}' | sort" + @echo + @echo 'Environment variables:' + @echo ' BUILD_TAG - set the build name used for e.g. artifact file naming' + @echo ' BB_DL_DIR - defines the directory (absolute path) where bitbake places downloaded files (defaults to bbcache/downloads)' + @echo ' BB_SSTATE_DIR - defines the directory (absolute path) where bitbake places shared-state files (defaults to bbcache/sstate-cache)' + @echo ' SETUP_ARGS - control advanced behaviour of the setup script (run ./device-software/setup.sh --help for more details)' + +############################################################################### +# Private targets +############################################################################### + +_no_targets: + +_check_setup_was_done: + @if [ ! -f $(CURDIR)/out/current/build/conf/local.conf ]; then echo Please run \"make setup\" first ; exit 1 ; fi + +_check_postbuild_was_done: + @if [ ! -f $(CURDIR)/out/current/build/toFlash/flashall.sh ]; then echo Please run \"make image/bootloader/kernel\" first ; exit 1 ; fi + +_setup-sdkhost_%: pub bbcache + @echo Setup buildenv for SDK host $* + @mkdir -p out/$* + ./device-software/setup.sh $(SETUP_ARGS) --dl_dir=$(BB_DL_DIR) --sstate_dir=$(BB_SSTATE_DIR) --build_dir=$(CURDIR)/out/$* --build_name=$(BUILD_TAG) --sdk_host=$* + @rm -f out/current + @ln -s $(CURDIR)/out/$* $(CURDIR)/out/current + @if [ $* = macosx ]; then /bin/bash -c "source out/current/poky/oe-init-build-env $(CURDIR)/out/current/build ; bitbake odcctools2-crosssdk -c cleansstate" ; echo "Please make sure that OSX-sdk.zip is available in your bitbake download directory" ; fi + +pub: + @mkdir -p $@ + +bbcache: + @mkdir -p bbcache + @mkdir -p $(BB_DL_DIR) + @mkdir -p $(BB_SSTATE_DIR) + +_image_archive: + cd $(CURDIR)/out/current/build/toFlash ; zip -r $(CURDIR)/pub/edison-image-$(BUILD_TAG).zip `ls` + cd $(CURDIR)/out/current/build/symbols ; zip -r $(CURDIR)/pub/symbols-$(BUILD_TAG).zip `ls` + +_devtools_package_archive: + cd $(CURDIR)/out/current/build/devtools_packages ; zip -r $(CURDIR)/pub/edison-devtools-packages-$(BUILD_TAG).zip `ls` + +_sdk_archive_%: + cd $(CURDIR)/out/$*/build/tmp/deploy/sdk ; zip -r $(CURDIR)/pub/edison-sdk-$*-$(BUILD_TAG).zip `ls *-edison-image-*` + +_toolchain_archive_%: + cd $(CURDIR)/out/$*/build/tmp/deploy/sdk ; zip -r $(CURDIR)/pub/edison-meta-toolchain-$*-$(BUILD_TAG).zip `ls *-meta-toolchain-*` + + +############################################################################### +# Continuous Integration targets: one per checkbox available in jenkins +# Each target places the the end-user artifact in the pub/ directory +############################################################################### + +ci_image: setup cleansstate devtools_package _devtools_package_archive image _image_archive + +_ci_sdk_%: + $(MAKE) _setup-sdkhost_$* cleansstate sdk _sdk_archive_$* + +_ci_toolchain_%: + $(MAKE) _setup-sdkhost_$* cleansstate toolchain _toolchain_archive_$* + +ci_sdk_win32: _ci_sdk_win32 +ci_sdk_win64: _ci_sdk_win64 +ci_sdk_linux32: _ci_sdk_linux32 +ci_sdk_linux64: _ci_sdk_linux64 +ci_sdk_macosx: _ci_sdk_macosx +ci_toolchain_win32: _ci_toolchain_win32 +ci_toolchain_win64: _ci_toolchain_win64 +ci_toolchain_linux32: _ci_toolchain_linux32 +ci_toolchain_linux64: _ci_toolchain_linux64 +ci_toolchain_macosx: _ci_toolchain_macosx + +ci_image-from-src-package-and-GPL-LGPL-sources_archive: setup src-package + cp $(CURDIR)/pub/edison-src-$(BUILD_TAG).tgz $(CURDIR)/out/current + cd $(CURDIR)/out/current ; tar -xvf edison-src-$(BUILD_TAG).tgz + cd $(CURDIR)/out/current/edison-src ; /bin/bash -c "SETUP_ARGS=\"$(SETUP_ARGS) --create_src_archive\" make setup cleansstate image _image_archive" + cd $(CURDIR)/out/current/edison-src/out/current/build/toFlash ; zip -r $(CURDIR)/pub/edison-image-from-src-package-$(BUILD_TAG).zip `ls` + cd $(CURDIR)/out/current/edison-src/out/current/build/tmp/deploy/sources ; zip -r $(CURDIR)/pub/edison-GPL_LGPL-sources-$(BUILD_TAG).zip `ls` + +ci_full: + $(MAKE) ci_image BUILD_TAG=$(BUILD_TAG) + $(MAKE) ci_image-from-src-package-and-GPL-LGPL-sources_archive BUILD_TAG=$(BUILD_TAG) + $(MAKE) ci_sdk_win32 ci_toolchain_win32 BUILD_TAG=$(BUILD_TAG) + $(MAKE) ci_sdk_win64 ci_toolchain_win64 BUILD_TAG=$(BUILD_TAG) + $(MAKE) ci_sdk_linux32 ci_toolchain_linux32 BUILD_TAG=$(BUILD_TAG) + $(MAKE) ci_sdk_linux64 ci_toolchain_linux64 BUILD_TAG=$(BUILD_TAG) + $(MAKE) ci_sdk_macosx ci_toolchain_macosx BUILD_TAG=$(BUILD_TAG) + diff --git a/device-software/utils/create-debian-image.sh b/device-software/utils/create-debian-image.sh new file mode 100755 index 0000000..6cf1aee --- /dev/null +++ b/device-software/utils/create-debian-image.sh @@ -0,0 +1,226 @@ +#!/bin/bash + +# Creates a debian rootfs image which can be flashed on Edison +# Requires that the host system has the following packages: debootstrap debian-archive-keyring python + +top_repo_dir=$(dirname $(dirname $(dirname $(readlink -f $0)))) +build_dir=$top_repo_dir/build + +function usage() +{ + echo "Creates a debian image starting from a sucessful yocto build" + echo "This needs to be run as root. It was tested successfully on Ubuntu 14.04+" + echo "On older versions, please install manually debootstrap >= 1.0.59" + echo "Options:" + echo -e "\t-h --help\t\tdisplay this help and exit" + echo -e "\t--skip_debootstrap\tavoids running deboostrap from scratch to save time." + echo -e "\t--build_dir\tspecify the yocto build directory. Defaults to ../../build" + echo "" +} + +skip_debootstrap="false" +add_graphical_packages="false" + +while [ "$1" != "" ]; do + PARAM=`echo $1 | awk -F= '{print $1}'` + VALUE=`echo $1 | awk -F= '{print $2}'` + case $PARAM in + -h | --help) + usage + exit + ;; + --skip_debootstrap) + echo "Skip running deboostrap from scratch to save time in case of re-run" + skip_debootstrap="true" + ;; + --add_graphical_packages) + echo "Add graphical packages in the rootfs. Requires a larger rootfs" + add_graphical_packages="true" + ;; + --build_dir) + build_dir=$(readlink -f "$VALUE") + ;; + *) + echo "ERROR: unknown parameter \"$PARAM\"" + usage + exit 1 + ;; + esac + shift +done + +cd $build_dir + +# Check the version of host debootstrap +debootstrap_version=`debootstrap --version | cut -d' ' -f2` +echo """import sys +from distutils.version import LooseVersion +if LooseVersion('1.0.59') <= LooseVersion(sys.argv[1]): + exit(0) +else: + exit(1)""" > tmp.py +python tmp.py $debootstrap_version +ok="$?" +rm tmp.py +if [ ! $ok -eq 0 ]; then + echo "Bootstrap version too old: needs >= 1.0.59, found $debootstrap_version" + exit -1 +fi + +# Check that .deb packages were properly created +if ! grep -q "package_deb" ./conf/local.conf; then + echo "No .deb packages were generated by bitbake (only .ipk). Please re-run setup.sh using the --deb_packages option." + exit -1 +fi + +# Re-run post build to avoid losing hairs +rm -rf ./toFlash +$top_repo_dir/device-software/utils/flash/postBuild.sh $build_dir + +echo "*** Start creating a debian rootfs image ***" + +ROOTDIR=jessie-chroot + +if [ "$skip_debootstrap" != "true" ]; then + rm -rf $ROOTDIR + mkdir $ROOTDIR + debootstrap --arch i386 --no-check-gpg jessie $ROOTDIR http://http.debian.net/debian/ +fi + +mkdir -p $ROOTDIR/home/root + +mount sysfs $ROOTDIR/sys -t sysfs +mount proc $ROOTDIR/proc -t proc + +# Backup config +cp $ROOTDIR/etc/environment $ROOTDIR/etc/environment.sav +cp $ROOTDIR/etc/resolv.conf $ROOTDIR/etc/resolv.conf.sav +cp $ROOTDIR/etc/hosts $ROOTDIR/etc/hosts.sav + +# Use host system network config to be able to apt-get later on +echo `export | grep http_proxy | sed 's/declare -x http_proxy=/Acquire::http::proxy /'`\; >> $ROOTDIR/etc/apt/apt.conf.d/50proxy +echo `export | grep https_proxy | sed 's/declare -x https_proxy=/Acquire::https::proxy /'`\; >> $ROOTDIR/etc/apt/apt.conf.d/50proxy +echo `export | grep HTTP_PROXY | sed 's/declare -x HTTP_PROXY=/Acquire::http::proxy /'`\; >> $ROOTDIR/etc/apt/apt.conf.d/50proxy +echo `export | grep HTTPS_PROXY | sed 's/declare -x HTTPS_PROXY=/Acquire::https::proxy /'`\; >> $ROOTDIR/etc/apt/apt.conf.d/50proxy +cp /etc/resolv.conf $ROOTDIR/etc/resolv.conf +cp /etc/hosts $ROOTDIR/etc/hosts + +CHROOTCMD="eval LC_ALL=C LANGUAGE=C LANG=C chroot $ROOTDIR" + +# Install necessary packages +$CHROOTCMD apt-get clean +$CHROOTCMD apt-get update +$CHROOTCMD apt-get -y --force-yes install dbus nano openssh-server sudo bash-completion dosfstools +$CHROOTCMD apt-get -y --force-yes install bluez hostapd file ethtool network-manager +$CHROOTCMD apt-get -y --force-yes install python + +# Necessary for the resize-rootfs service +$CHROOTCMD apt-get -y --force-yes install e2fsprogs + +# This service is added by the network-manager debian package but we don't want it activated +# as it causes an UART console corruption at boot +$CHROOTCMD rm /etc/systemd/system/multi-user.target.wants/ModemManager.service /etc/systemd/system/dbus-org.freedesktop.ModemManager1.service + +# Create a default user "user" with password "edison" +# Encrypted password is created with mkpasswd +$CHROOTCMD useradd -m user -p YyleQbUNcJwao +$CHROOTCMD adduser user sudo +$CHROOTCMD adduser user netdev +$CHROOTCMD chsh -s /bin/bash user + + +# Fixup watchdog in systemd +echo "RuntimeWatchdogSec=90" >> $ROOTDIR/etc/systemd/system.conf + +# Install kernel/modules/firmware base packages generated by yocto +cp -r tmp/deploy/deb $ROOTDIR/tmp/ +$CHROOTCMD dpkg -i /tmp/deb/edison/kernel-image-3.10.17-poky-edison+_1.0-r2_i386.deb +$CHROOTCMD dpkg -i /tmp/deb/edison/kernel-3.10.17-poky-edison+_1.0-r2_i386.deb +$CHROOTCMD dpkg -i /tmp/deb/edison/kernel-module-bcm4334x_1.141-r47_i386.deb +$CHROOTCMD dpkg -i /tmp/deb/edison/kernel-module-bcm-bt-lpm_1.0-r2_i386.deb +$CHROOTCMD dpkg -i /tmp/deb/edison/kernel-module-libcomposite_1.0-r2_i386.deb +$CHROOTCMD dpkg -i /tmp/deb/edison/kernel-module-u-serial_1.0-r2_i386.deb +$CHROOTCMD dpkg -i /tmp/deb/edison/kernel-module-usb-f-acm_1.0-r2_i386.deb +$CHROOTCMD dpkg -i /tmp/deb/edison/kernel-module-g-multi_1.0-r2_i386.deb +$CHROOTCMD dpkg -i /tmp/deb/edison/kernel-module-aufs_1.0-r2_i386.deb +$CHROOTCMD dpkg -i /tmp/deb/all/bcm43340-fw_6.20.190-r2_all.deb +$CHROOTCMD dpkg -i /tmp/deb/core2-32/bcm43340-bt_1.0-r0_i386.deb + +# Enables USB networking at startup +cat > $ROOTDIR/lib/systemd/network/usb0.network < $ROOTDIR/lib/systemd/system/sshdgenkeys.service < /dev/null ; then rm /etc/ssh/*_key* ; ssh-keygen -A ; sync ; fi" +Type=oneshot +RemainAfterExit=yes +EOF + +# Set up uboot env configuration +cat > $ROOTDIR/etc/fw_env.config <> $ROOTDIR/etc/hosts +echo "edison" > $ROOTDIR/etc/hostname +echo "rootfs / auto nodev,noatime,discard,barrier=1,data=ordered,noauto_da_alloc 1 1" > $ROOTDIR/etc/fstab +echo "/dev/disk/by-partlabel/boot /boot auto noauto,comment=systemd.automount,nosuid,nodev,noatime,discard 1 1" >> $ROOTDIR/etc/fstab + +# Clean up +umount -l -f $ROOTDIR/sys +# Kill remaining processes making use of the /proc before unmounting it +lsof | grep $ROOTDIR/proc | awk '{print $2}' | xargs kill -9 +umount -l -f $ROOTDIR/proc +rm -rf $ROOTDIR/tmp/deb + +# Create the rootfs ext4 image +rm edison-image-edison.ext4 +fsize=$((`stat --printf="%s" toFlash/edison-image-edison.ext4` / 524288)) +dd if=/dev/zero of=edison-image-edison.ext4 bs=512K count=$fsize +mkfs.ext4 -F -L rootfs edison-image-edison.ext4 + +# Copy the rootfs content in the ext4 image +rm -rf tmpext4 +mkdir tmpext4 +mount -o loop edison-image-edison.ext4 tmpext4 +cp -a $ROOTDIR/* tmpext4/ +umount tmpext4 +rmdir tmpext4 + +cp edison-image-edison.ext4 toFlash/ +# Make sure that non-root users can read write the flash files +# This seems to fix a strange flashing issue in some cases +chmod -R a+rw toFlash diff --git a/device-software/utils/create_devtools_package.sh b/device-software/utils/create_devtools_package.sh index b5d9407..d1d74b0 100755 --- a/device-software/utils/create_devtools_package.sh +++ b/device-software/utils/create_devtools_package.sh @@ -4,17 +4,32 @@ echo "*** Start creating the dev tools ipk packages ***" top_repo_dir=$(dirname $(dirname $(dirname $(readlink -f $0)))) -cd $top_repo_dir/build +build_dir="" +if [ $# -eq 0 ]; then + build_dir=$top_repo_dir/build +else + build_dir=$1 +fi + +cd $build_dir bitbake peeknpoke bitbake powertop bitbake bonnie++ bitbake wfa-tool +# for iotkit-comm ACS sanity tests +bitbake gcc +bitbake iotkit-comm-c +bitbake iotkit-comm-js rm -rf devtools_packages mkdir devtools_packages -cp $top_repo_dir/build/tmp/deploy/ipk/core2-32/peeknpoke_1.1-r0_core2-32.ipk devtools_packages/ -cp $top_repo_dir/build/tmp/deploy/ipk/core2-32/powertop_2.5-r0_core2-32.ipk devtools_packages/ -cp $top_repo_dir/build/tmp/deploy/ipk/core2-32/bonnie++_1.03c-r0_core2-32.ipk devtools_packages/ -cp $top_repo_dir/build/tmp/deploy/ipk/core2-32/wfa-tool_r4.0-0_core2-32.ipk devtools_packages/ +cp tmp/deploy/ipk/core2-32/peeknpoke_1.1-r0_core2-32.ipk devtools_packages/ +cp tmp/deploy/ipk/core2-32/powertop_2.5-r0_core2-32.ipk devtools_packages/ +cp tmp/deploy/ipk/core2-32/bonnie++_1.03c-r0_core2-32.ipk devtools_packages/ +cp tmp/deploy/ipk/core2-32/wfa-tool_r4.0-0_core2-32.ipk devtools_packages/ +# for iotkit-comm ACS sanity tests +cp tmp/deploy/ipk/core2-32/gcov_4.8.2-r0_core2-32.ipk devtools_packages/gcov_core2-32.ipk +cp tmp/deploy/ipk/core2-32/iotkit-comm-c-tests_0.1.1-r2_core2-32.ipk devtools_packages/iotkit-comm-c-tests_core2-32.ipk +cp tmp/deploy/ipk/core2-32/iotkit-comm-js-test-dependencies_0.1.1-r2_core2-32.ipk devtools_packages/iotkit-comm-js-test-dependencies_core2-32.ipk diff --git a/device-software/utils/flash/flashall.bat b/device-software/utils/flash/flashall.bat index 3d18dd5..05269f5 100644 --- a/device-software/utils/flash/flashall.bat +++ b/device-software/utils/flash/flashall.bat @@ -4,7 +4,6 @@ set BASE_DIR=%~dp0 set USB_VID=8087 set USB_PID=0a99 set /a TIMEOUT=60 -set DO_DFU_FLASH=1 ::Phone flash tools configuration part set DO_RECOVERY=0 @@ -14,10 +13,9 @@ set PFT_XML_FILE="%BASE_DIR%pft-config-edison.xml" :: Handle Ifwi file for DFU update set IFWI_DFU_FILE=%BASE_DIR%edison_ifwi-dbg -set DO_LIST_VARIANTS=0 set VAR_DIR=%BASE_DIR%u-boot-envs\ -set VARIANT_NAME_DEFAULT=edison-default -set VARIANT_NAME_BLANK=edison-blank +set VARIANT_NAME_DEFAULT=edison-defaultrndis +set VARIANT_NAME_BLANK=edison-blankrndis set VARIANT_NAME=%VARIANT_NAME_BLANK% set LOG_FILENAME=flash.log @@ -29,26 +27,12 @@ set argcount=0 set appname=%0 :parse_arg_start if -%1-==-- goto parse_arg_end -if -%1- == --i- set /a DO_RECOVERY=1 -if -%1- == --d- set /a DO_DFU_FLASH=1 if -%1- == ---recovery- ( set /a DO_RECOVERY=1 - set /a DO_DFU_FLASH=0 ) if -%1- == ---keep-data- ( - set /a DO_RECOVERY=0 set VARIANT_NAME=%VARIANT_NAME_DEFAULT% ) -if -%1- == --b- ( - set /a DO_RECOVERY=1 - set /a DO_DFU_FLASH=1 - set VARIANT_NAME=%VARIANT_NAME_BLANK% -) -if -%1- == --l- set /a DO_LIST_VARIANTS=1 -if -%1- == --t- ( - set VARIANT_NAME=%2 - shift -) if -%1- == -/?- set /a show_help=1 if -%1- == --h- set /a show_help=1 if -%1- == ---help- set /a show_help=1 @@ -64,21 +48,6 @@ if %show_help% == 1 ( exit /b ) -:: U-boot target listing part -if %DO_LIST_VARIANTS% == 0 goto :skip_list_variants -echo Availables U-boot targets: -:: list all .env file in var_dir -for /R "%VAR_DIR%" %%i in ( *.bin ) do ( - :: echo the file-name only without extension - echo %%~ni -) -exit /b 5 -:skip_list_variants - -if %DO_DFU_FLASH% == 1 goto do_init_log -if %DO_RECOVERY% == 1 goto do_init_log -goto skip_init_log -:do_init_log if %verbose_output% == 1 goto skip_init_log echo ** Flashing Edison Board %date% %time% ** >> %LOG_FILENAME% :skip_init_log @@ -97,11 +66,11 @@ call:flash-ifwi if %errorlevel% neq 0 ( exit /b %errorlevel%) echo Recovery Success... echo You can now try a regular flash +goto :skip_flash_kernel :skip_flash_ifwi :: ******************************************************************** :: Kernel & rootfs flashing part -if %DO_DFU_FLASH% == 0 goto :skip_flash_kernel echo Using U-boot target: %VARIANT_NAME% set VARIANT_FILE="%VAR_DIR%%VARIANT_NAME%.bin" if not exist %VARIANT_FILE% ( @@ -159,8 +128,12 @@ call:flash-command --alt u-boot-env0 -D %VARIANT_FILE% if %errorlevel% neq 0 ( exit /b %errorlevel%) echo Flashing U-Boot Environment Backup -call:flash-command --alt u-boot-env1 -D %VARIANT_FILE% +call:flash-command --alt u-boot-env1 -D %VARIANT_FILE% -R if %errorlevel% neq 0 ( exit /b %errorlevel%) +echo Rebooting to apply partiton changes +call:dfu-wait +if %errorlevel% neq 0 ( exit /b %errorlevel%) + echo Flashing boot partition ^(kernel^) call:flash-command --alt boot -D "%BASE_DIR%edison-image-edison.hddimg" @@ -173,37 +146,23 @@ if %errorlevel% neq 0 ( exit /b %errorlevel% ) echo Rebooting echo U-boot ^& Kernel System Flash Success... if %VARIANT_NAME% == %VARIANT_NAME_BLANK% ( - echo Your board needs to reboot twice to complete the flashing procedure, please do not unplug it for 2 minutes. + echo Your board needs to reboot to complete the flashing procedure, please do not unplug it for 2 minutes. ) :skip_flash_kernel :: ******************************************************************** :: The End -if %DO_RECOVERY% == 0 ( -if %DO_DFU_FLASH% == 0 ( - echo Nothing to do - call:print-usage %appname% -)) - exit /b 0 :print-usage - echo Usage: %1 [-hl] [--help] [--recovery] [--keep-data] [-m][-ipdb] [-t uboot-env] + echo Usage: %1 [-h] [--help] [--recovery] [--keep-data] echo Update all software and restore board to its initial state. echo -h,--help display this help and exit. echo -v verbose output echo --recovery recover the board to DFU mode using a dedicated tool, echo available only on linux and window hosts. echo --keep-data preserve user data when flashing. - echo. - echo. - echo deprecated commands: - echo -i flash IFWI - echo -d flash U-boot, U-boot Environment, Linux Kernel, Rootfs - echo -b blank the device (eq -i -d -t %VARIANT_NAME_BLANK%) - echo -l list availables U-boot target environments and exit - echo -t uboot-env specify U-boot target environments to flash (default is %VARIANT_NAME%) exit /b 5 :flash-dfu-ifwi @@ -218,7 +177,7 @@ exit /b 0 set filterout="on" if %verbose_output% == 1 ( set filterout="off") - dfu-util -d %USB_VID%:%USB_PID% %* 2>&1 | cscript.exe //B filter-dfu-out.js %LOG_FILENAME% %filterout% + dfu-util -d %USB_VID%:%USB_PID% %* 2>&1 | cscript.exe /E:JScript //B filter-dfu-out.js %LOG_FILENAME% %filterout% set /a err_num=%errorlevel% if %err_num% neq 0 echo Flash failed on %* diff --git a/device-software/utils/flash/flashall.sh b/device-software/utils/flash/flashall.sh index 8c50ff3..563769d 100755 --- a/device-software/utils/flash/flashall.sh +++ b/device-software/utils/flash/flashall.sh @@ -6,25 +6,36 @@ IFS=$(echo -en "\n\b") GETOPTS="$(which getopt)" if [[ "$OSTYPE" == "darwin"* ]] ; then READLINK=greadlink; GETOPTS="$(brew list gnu-getopt | grep bin/getopt)"; else READLINK=readlink;fi; -BASE_DIR="$(dirname $($READLINK -f $0))" -ESC_BASE_DIR=${BASE_DIR/' '/'\ '} + +if [[ "$OSTYPE" == "cygwin" ]] ; +then + TEMP_DIR="$(dirname $($READLINK -f $0))" + ESC_BASE_DIR="$(cygpath -m ${TEMP_DIR})" + BASE_DIR="$(cygpath -m ${TEMP_DIR})" +else + BASE_DIR="$(dirname $($READLINK -f $0))" + ESC_BASE_DIR=${BASE_DIR/' '/'\ '} +fi; USB_VID=8087 USB_PID=0a99 TIMEOUT_SEC=60 DO_RECOVERY=0 -DO_DFU_FLASH=1 # Phone Flash tools configuration files PFT_XML_FILE="${BASE_DIR}/pft-config-edison.xml" # Handle Ifwi file for DFU update IFWI_DFU_FILE=${ESC_BASE_DIR}/edison_ifwi-dbg -DO_LIST_VARIANTS=0 VAR_DIR="${BASE_DIR}/u-boot-envs" -VARIANT_NAME_DEFAULT="edison-default" -VARIANT_NAME_BLANK="edison-blank" +if [[ "$OSTYPE" == "cygwin" ]] ; then + VARIANT_NAME_DEFAULT="edison-defaultrndis" + VARIANT_NAME_BLANK="edison-blankrndis" +else + VARIANT_NAME_DEFAULT="edison-defaultcdc" + VARIANT_NAME_BLANK="edison-blankcdc" +fi VARIANT_NAME=$VARIANT_NAME_BLANK LOG_FILENAME="flash.log" @@ -32,21 +43,13 @@ OUTPUT_LOG_CMD="2>&1 | tee -a ${LOG_FILENAME} | ( sed -n '19 q'; head -n 1; cat function print-usage { cat << EOF -Usage: ${0##*/} [-hl][--help][--recovery] [--keep-data] [-m][-ipdb] [-t uboot-env] +Usage: ${0##*/} [-h][--help][--recovery] [--keep-data] Update all software and restore board to its initial state. -h,--help display this help and exit. -v verbose output --recovery recover the board to DFU mode using a dedicated tool, available only on linux and window hosts. --keep-data preserve user data when flashing. - - - deprecated commands: - -i flash IFWI. - -d flash U-boot, U-boot Environment, Linux Kernel, Rootfs. - -b blank the device (eq -i -d -t ${VARIANT_NAME_BLANK}). - -l list availables U-boot target environments and exit. - -t uboot-env specify U-boot target environments to flash (default is ${VARIANT_NAME}). EOF exit -5 } @@ -117,7 +120,9 @@ function flash-ifwi-xfstk { function dfu-wait { echo "Now waiting for dfu device ${USB_VID}:${USB_PID}" - echo "Please plug and reboot the board" + if [ -z "$@" ]; then + echo "Please plug and reboot the board" + fi while [ `dfu-util -l -d ${USB_VID}:${USB_PID} | grep Found | grep -c ${USB_VID}` -eq 0 ] \ && [ $TIMEOUT_SEC -gt 0 ] && [ $(( TIMEOUT_SEC-- )) ]; do @@ -128,47 +133,31 @@ function dfu-wait { then echo "Timed out while waiting for dfu device ${USB_VID}:${USB_PID}" flash-debug - echo "Did you plug and reboot your board?" - echo "If yes, please try a recovery by calling this script with the --recovery option" + if [ -z "$@" ]; then + echo "Did you plug and reboot your board?" + echo "If yes, please try a recovery by calling this script with the --recovery option" + fi exit -2 fi } # Execute old getopt to have long options support -ARGS=$($GETOPTS -o hvlt:eidb -l "keep-data,recovery,help" -n "${0##*/}" -- "$@"); +ARGS=$($GETOPTS -o hv -l "keep-data,recovery,help" -n "${0##*/}" -- "$@"); #Bad arguments if [ $? -ne 0 ]; then print-usage ; fi; eval set -- "$ARGS"; while true; do case "$1" in - -l) shift; DO_LIST_VARIANTS=1;; - -t) shift; if [ -n "$1" ]; then VARIANT_NAME=$1; shift; fi;; -h|--help) shift; print-usage;; -v) shift; OUTPUT_LOG_CMD=" 2>&1 | tee -a ${LOG_FILENAME}";; - -i) shift; DO_RECOVERY=1;; - -d) shift; DO_DFU_FLASH=1;; - -b) shift; DO_RECOVERY=1; DO_DFU_FLASH=1; VARIANT_NAME=$VARIANT_NAME_BLANK;; - --recovery) shift; DO_RECOVERY=1;DO_DFU_FLASH=0;; - --keep-data) shift; DO_RECOVERY=0; VARIANT_NAME=$VARIANT_NAME_DEFAULT;; + --recovery) shift; DO_RECOVERY=1;; + --keep-data) shift; VARIANT_NAME=$VARIANT_NAME_DEFAULT;; --) shift; break;; esac done -if [ ${DO_LIST_VARIANTS} -eq 1 ]; -then - echo "Availables U-boot targets:" - for variant in $(ls ${VAR_DIR}) ; do - #echo just filname without extension - echo "${variant%.*}" - done - exit -5 -fi - -if [[ ${DO_RECOVERY} -eq 1 || ${DO_DFU_FLASH} -eq 1 ]]; -then - echo "** Flashing Edison Board $(date) **" >> ${LOG_FILENAME} -fi +echo "** Flashing Edison Board $(date) **" >> ${LOG_FILENAME} if [ ${DO_RECOVERY} -eq 1 ]; @@ -189,10 +178,8 @@ then flash-ifwi echo "Recovery Success..." echo "You can now try a regular flash" -fi -if [ ${DO_DFU_FLASH} -eq 1 ]; -then +else echo "Using U-Boot target: ${VARIANT_NAME}" VARIANT_FILE="${VAR_DIR}/${VARIANT_NAME}.bin" if [ ! -f "${VARIANT_FILE}" ]; then @@ -233,7 +220,9 @@ then flash-command --alt u-boot-env0 -D "${VARIANT_FILE}" echo "Flashing U-Boot Environment Backup" - flash-command --alt u-boot-env1 -D "${VARIANT_FILE}" + flash-command --alt u-boot-env1 -D "${VARIANT_FILE}" -R + echo "Rebooting to apply partition changes" + dfu-wait no-prompt echo "Flashing boot partition (kernel)" flash-command --alt boot -D "${ESC_BASE_DIR}/edison-image-edison.hddimg" @@ -244,14 +233,8 @@ then echo "Rebooting" echo "U-boot & Kernel System Flash Success..." if [ $VARIANT_NAME == $VARIANT_NAME_BLANK ] ; then - echo "Your board needs to reboot twice to complete the flashing procedure, please do not unplug it for 2 minutes." + echo "Your board needs to reboot to complete the flashing procedure, please do not unplug it for 2 minutes." fi fi -if [[ ${DO_DFU_FLASH} -eq 0 && ${DO_RECOVERY} -eq 0 ]]; -then - echo "Nothing to do ..." - print-usage -fi - IFS=${BACKUP_IFS} diff --git a/device-software/utils/flash/ifwi/edison/edison_dnx_fwr.bin b/device-software/utils/flash/ifwi/edison/edison_dnx_fwr.bin index c251d5d2c043e8336f12214be4877f4b0165c4ac..e646293a8b1540ae25202c3afb269eb1cf603a46 100644 GIT binary patch delta 570 zcmV-A0>%B5{{@u)1r5o|zf`=^tN{N10007$p*eK1flC2@|JLdM+o;+@sGa`vriQbr zsWsJAu<4O9xMnUCN8eo?bi_WK2GxKbcEK6u?#oG|)DAQ0&-A4xU_IqorupZx_S&)% zqQZ~p3c~YZtqQ!0FFPH|LwW^o7;m{5g`;tCUa_Beg*Xe9RsOB|>!ube^KB_yUSqZdg#k`h%!}$@1VMfah9k~ zsd4GS++8x((HOe2?y0s?JU`?GF0T?Du0v$T_`O`dPz#vLSdIsy!j(1!G(4X(N2(6N z4D*iB3cIdHi8ILQYdb6b@9!0PqtATJ$rGGmDic7Q10?2j*%FDl0vzU3v0e_8Z zmgl-tz3 zGP9kj_`w4;m=0ujmPcDtV0gX(&BgJ{()my_qKJfImN1y>?Kf|QFM5s4k5I@!kUbO; zFF6gAxFv0rwKw=s)EH3-FWqk~Cx1Y?9hKD10*b?}cSIqNjlD5w&}6pqTEOQT2k3K4)i4`^`xf4`+8P6`Q z{7~QzwZiWgVJXd-jQQ$;zqd~Jo^((F6Bl#zF;e^Y$}j(aAOTk=r{D$$XaduBgJ68O IV0-~I17t2BSpWb4 delta 570 zcmV-A0>%B5{{@u)1r2hXwNt#(tN{N10007$p*eK1flC2@BN8kK_mwArlWHq9XB5*W z@dJF@s%EWC$=5744)~zvN*d>A(b%l7pA=VU`%BC>u*oSX+_$-Y|363j?m|Vr=TB8t z{M~ACq*(1sMz@iF#xBoA?cphJ%OQT7j7oH=P-5lPh#jfDWQkWNDhbWKlON+)*wN}s}dKsEs#W8u2%ao0*(}5gC%qRtOi_@lh}N_?U%@#y)+PZ z!fN5z3TW_lt{@LRS&t~*q2r1n4uXxd4XmPCfNK{5k_5BN0?2j*bDXtPvzU3v0e`nl zd&rZ}iFPS+USLbl2SvyobLo7H_0%BVqQimAyneEgMSZ|}#^8mYFW`&8UyWG6%1q3* zMtqUf0U)7KJVbu?jtw8m&1mAss!+!FVuaTqa@>pUzD=^RnH?HHWEgy$+!bTd__Tg1Q7dd z^@{{ZOA0+nwl$kJ!%UtQ!?I2}k0#dPYa$1AH)1#lYDC7t_k^(%q1K6Gy)}Qh9#+&+m9#EX-K2@3IahrgJ68O IV0-~I14&jDx&QzG diff --git a/device-software/utils/flash/ifwi/edison/edison_dnx_osr.bin b/device-software/utils/flash/ifwi/edison/edison_dnx_osr.bin index 2cff3a048c12c38d12e24275a044c63ddaba8c94..a0e41b55a7c22f28c4818e9ef275559a174f82de 100644 GIT binary patch delta 576 zcmV-G0>Ax)j0uE{2@T83zf`=^tN{N10007&5dn0uflC2@VbXf-uJ1*J7{Y;TEz3<} zz&jA*olwkE-_X_JKdYsf(_KAxM=#etXwJ&`0h>3y}QaBPS{z4=WM#MTm+!WS0|pM#Zyq*S1!B5PTJvWw)>M9Q$;>Yv4Z-%0@Nix zrp32-7d~aaa2OiZJxVDPVS8okFauYl1I~m<(;Xif+Y2LV#{lhEqM)t|&@VL@ za5zN~^K2XuHQ#iS=4^uDe`7Wm^Gdsd*UVq~%dy_{`-`E0G65~a6Z-!9*kWW3r3sbE3t#nvZ|U1pI^TwH!=8CszTkvCk{|7`qNs=*c6o zs?`FKnj@3_1!;Blku#2KLf}aMxG$t`lp}eV&7CL1yKQM2SnN|Wb OlZVhk0k_aX0yG1V1Q-qg delta 576 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 YSdkGp5IjIt#6?a-P(xEhLI3~&03@XCjsO4v diff --git a/device-software/utils/flash/ifwi/edison/edison_ifwi-dbg-01.bin b/device-software/utils/flash/ifwi/edison/edison_ifwi-dbg-01.bin index 72109c47b16182cb66407520407874cadb6cb3b8..e9b8211d1732e4f7c632fbcc32e06e05dfd05b76 100644 GIT binary patch delta 24875 zcmce;c{o*F^f-R+b)9o@?tR zAMUwKC1q>K;a64@Quif&S~z$}znABSAJF_&I`H7LN7_C%5#yhZTeYRkIpo}(!EQIV zx{r;n$!NA7#f+=9TRmdH#`x?hXZi$Y9B9bcHASu(9?sh|L0-Y*3Pt?j_?^!#Xq$62 zcmGRMJXTY3QyuOeuJnwx=f|h!D^ZIc zcf6{Vm422$?$(n_a#b~r-*Gv+w7Grd^YpoCB>Ive(3hK~ z?CWh@BzF4xe1@0rzSEOPvkQMc&tJ8cTnAt)j{0%(PR5_-?$>d8%as(^~}cF#0Vjqa51+v8t<@=5w<`-l7! zYaZ}Vf142+*u3!5u?wZCCh@1N?hLK1PEKgaINeq(zPCDGedXt(EAQr9D74?S@a>YQ z9Wkq3?7#Xjd*$?nhM`*bm(2Z8w&O+gtg(fPg}eB<<;M!Om&~u2Ib*c5>~OC&TaEe+ ztyhV?^TDI3JX!kusrIfTU(Ro~N_sS|qxHFLz7+q|BW z$dIBnJJ)|x^Heb}d2w~~-}Ak1t{Jp5r`zu58^29hx%K2r(UzyG2OAnz7hP%ZKehLj zbNu1MmTu2A44Bbi(A42CFO2E#-d0)a2D z*sbdDLw)am)wGp}=kl>3ZqwG0j@zHu$KIs1BbbQf$jne29kl1zP2H< z$Iw}((f&){Ca3L7KbX34qgp`e4RTlIvF%GAY|Naz`LBIO&&~+U4bxKe76nO9{xEX% zS3iD6J=gt+Pj<}E#f5<}^3t?D?OnXWTyZN@yU694wo12;VCFMKw7 z+x|PnBA%ar()MNbI#GPad6y?-(wC-!M*~&`Hn;B?F!ya>WJBxha99F3 zwVl5hzjzMEeEyh`SX&t82SEh_JB%-AYjJn)Bm_JwE2O@Y=%-2Zw3);bQDRdE{n;G3 z?Ic{S-&TtldQ2dPLs>MD+Dv{2kR+6F_#BQzQX^s{7%v&14n&8|Gbr9LN4S`td*mRk%bNV zYWKBrSZX$LlqC?T-4{bC%uh?Mdzd8@0gUDk0numyh#sB60X;{j%fxmK9exi`WNsg2Cm2?oJ}$C1YavjjmzQ0a3mZN3?~k!p%Xr)4Bg8OJ;)?_ zN_lRq;rt;utj|a|9zf9I`UhdkmJ|X)42b*xAe1cc1ItB#kSh`N(r0)8XyeC@rjH=?V}!ZMb|FiV$f(AujE~cVo{izrA7Cip zv2+uSJx286y|h7djuFm7c`zSM438NZOpk+9Vu_H6Gh*V*gXsjA?0}vk6}=J_9%6B5 zFuet8mU)|(kwK{T7-1?^U=rGc0()*r0-Vye6Wc~CCn8!d{|mW8i3|;v#R=$OPbk&N zrHbP;wM_2^28t!)!0Z$jiJiPSb!>$ja8@trn=+S!a{>%kVCy{VgJlqcWHRX@9s1Wu zn0`XVl5RHvw;gbRNUDn+PHRIgNd9KI5!>DE25?xek?N{V zxh4sLz+)u$TjCGXxTk2<3Brh5fc8Kn9R$S1A%|^P{=o0B?eA2&-Jg^bo05eH65F}4 zdg-o^O>F1I3ez1Rl~fh0l6VV-b4Yah@P8BPN2j}YQEEn~5B(pcGF=arGyvu=Xh#vB z1C1P|Ed4Kt?|&faV_5*yWnVxwUjeJ&gn?Mp-=9xKr;n>b@h8C=#Z~CQNy3mfp$c6& zNw^I-2ot`dn>f{55W^LN@gf)nxXZl->MbngZ~?2z;eFC7 zY~ag)RFv=%pXVr1u9cn$w^cdw{vqLXL1ZDq->|Ok0Y)s4%krYO@?o5^<%2Q#=ZDRUj>Hx6}+`cz1(R=-Tr*%+%A!=VCN18FND3zr47 zPAUUkaycUti$#e)JmRWTqrOaLO%Kn>0y^d`14~LP$CLp>kH5>M*tD1q1vscusYjH2 znz$~dHz*h;p?EL{+S|yN^{}f_`$YACVLf?jM9TueVcQRuMF{AK|49fMW;z*`G<>pBnB`F?*C#29)n5ms$W~YN_R=P4(QKN{dh4rw!Sf(q4bjaKy zBdR$xI5ip5K?Sln5w%B|7E*g$0hm-pW%)u}7C#EU=B8ZzSYfIsGq=EqO7;MV=VXdy zH5Ga(9rFi+5an_?$nPv+DPpU?@VcvdVIx|1mI$z-@f={`!CIf96T}5$Tn*Ffq={NN z_gRsHlD(VgTp?;ZOY}C>2d*-nCNxNe;-z|jkvSZ|ATeEw?9UOsNoA%4g+WHD%w)Ag zKY726rhM$cOk_H20VR)kiYMcG7FfBkymM6I26pKLx<$6g0n{y~d!sk!2`8zgRt~n1|EW&4OPXY^U0qVt28s`9 zl8tu2X3VgR8MXV=XyDZFhw2AC_Sg9EvA4@lWpsi?9fEJ20<;5}H9^p5YaX@FS{AQ?Dars~^>A};CHYibyHdT8lqQs_zeLyt`vNG~H#o*lqCZ$$$&$Wm&(4M|pV`J> z$V*2TG6nU8#+zpq|uBL8Twc^I1 zLzju38W^S$P)ivDpzt!GYxG+nPyyR`nRgTAhIImML!T}a7JMupD)1t8kzF>?hqtvJ zO~@wt@d}=y)!D=l5;r0xJ??<`rou8+EH=)td?{9sWqflnKw%~NU({W0yvobeZnZx1$ZlW?bT$%I#$e{Kd!dtfq7DLL@iHZ=?IhVLLW;JvZJ63K5bKxN)^$Qj1U?FT+{EHi6qQHx=Y1?ioAZcX{Vd9$hCv{rvWQ`-SRnIorF7SW zgc)z46=fcFi~6H`d4v(m-XoR|$CZAVl@%0~TmnHPE;w}-e{#!^=qh2%i*H1>SBbt3 z@wlNYloHt&fWGja+P+HBQ-ZOfSOKM*41@}G&%|82I<(;`(R=11Xr;^% zgTZvvU@Mj?hAkn6riMZSBk-=A|8Ntoq62g^x71n|fDc+!vMcbG1?W)8)@%YcUSlBv zyDIpmZnea~c&9of$R~VFaCuIwhO@)dt&ocAu5gj@zv6ma_+MkI(1Luz$P&|dP)abR zB*YrPV$oL0?ve4IZfZmq^ND_@Cz@c&E5N&TI6xX_LJp3R3LQ9-GD|(^l1Hd5p9tff zXhMP42rmJ~Q$Qt8yGCg7c$rP;>@X7HA+fWq~-S&`(cefu>Ro7MNCrY72U%APV82>9Ax*~5i` zLZXCMUnU<|M1*m9ZDnYEG2zSG?Tqdg6BfeOW)b5P1aqYXAWSh~-s?yMJCQe#Hq|B zTwRR2{Wto-6O>d*MBC2kqU$O)3DW_Wu44XOSYVT9MmxaTq5z|p6XvqWOEKjRSZdOI~7krKMsAAYNY5fZ@5&)?ym^_%<8R`f4UaO1Dbd->c=WQ{MeB5M zBRA9#y`=eAO;h!w6Mr&V>oZ}>lxbpmmvKgMn$a%Cn32}{O#qLUv0N$58bE8wi{=zu zhVIdlyJ5z%_%s1HSSj6l$P_jRDYsfkWZZ{OP>|#nL@}z2ZULr|>EZ%lXVDaF`&B?X zwB{i(f;X!MU4KZJ^b!J5LJ|H_u+a!-F`xm83DEb4#30=UC~nVC0riT$_~5|L zV^PkJV95ZIAT$k+nF3mEm0X#%SK5wXq|%PSjagk@!0#L7zPVzHws zW84f_npna4Sa5XuZ^F^J1PXED++>g^I!^;-7QPW?Q~-B4$AIIYOchtbF@?smV*bl}!1j}qY>iOT^a}l~ z1rwN7gA5)MqXTiDbjJ1t`$_fUGLE?Cqcd3DJwhSbEk~wDbuWCm7lFC&YyD{R3gBdH^}>Dm*$;!#mQ# z}0XP)HVSmqp1r7L^3g0^)^1F5(?kGLeuMr z9+>&EI>K+vVcaS3SC)|3l~Qz{Q;r)>ZNoTrf@6f!Yd)^-qb>*yt*s z66QQGmd{TK=MmN5a{@E}eTvWHaPZ8Qi#r>1X2EG?z!>xrZ;`OqP2s}`=|+K#gS`A* z9FaDI<~}1L)$x!4CV05kJR@AJ^1!NOdAacQ(UL#6dgc-*SmLR&2K-@y!WVGtWl`Le ztOv;HInfu(6!Dy}!Hr3MPOR@+3k8?y!svv!@PjZq;WY!3n_B~9=_PRZ!u2>b5Pdbz zK)ozBp%5B8tASXE&F^6Y;b?)?DD%+QlGo1pjI|6dOqbnQ8%ul5tsBIBBGcIgMQmRX zbAn$2UoH8kXlGqq!*a179)H*tbulJ2lqr0nM}01QDO9^1FU;nGW_X2-LYPIcJh#D^ z(=mBrgI->Dn1R%A7b3+AqBrl10100bz2GVz97@z~EG<0`==1b|4pWVIN!a0@So{(! z1tj0_k{HngPX`H&oiOgy?x_8wjIu$_+Lwf_khQmFnTJjW+`{!}Bo^D0c?+?R6kooy z3o{>eKnUE0g2V=HVjI_>AhCeQ4i7}VCWcD8pMyoHR4fjb;7d<>7OY8Z0WiaF9s({E zCy3Qh8g)zCSy;DNFI7teBtc2RfobiN!Qlxd!M$y+tjUk>?8{%>Q>*U zSSr-M7lXHp6U+Unyy-&jG(l*jVk}-(cb!G!X+jsbD4HflN`q&BlG*FqCQvsm01xI| z-6q)kS||7lFr$nwP&c&>Z;EpndpvqfVM9>1ZWI7;xiVEO9P|vNSbeZ54s;iWW$+=< zHS@I;!1NfxHUWH;uq_vhB4+=YMj~V$BIRz_z7AigY(E3u+gC8i!MD%^=ThpU*#qZ~ zarpQIIjd^6HH(1=8cjz*4Ev0Kky%x%7`H;6CG$IjQd@{#A}b)Ho+o~1s(h9MXiA=NgJNnQ<^tZ${(|Jk)g&8nY`z)qUK|Y*{xS#5Me-ild+4ao*4I!3d z$()O0yFrKHRCV0!EUtoQU!av!K)K$;SjA9hR?Ik?ix^Xt98y4QV)k!^DIq{tC8mrX zvdwjUi0SDsr&!@M;!2=47|elCZy*&{BaRU$HvqWL)tyyw8KkKo47;s{i|$bSs$lza znQ?MGa#RrZ(oFzMH*JHtPSYH|u+)lVjzVyLcv5y5IOf(m-(LMz3|HRwVKrp^BD58B z7x@0tj+Rh32D`OK2El^PIK~2se!#AhrnDX0#if4%Ip9<&P?|B*z{aRq zL1^=8YEYYk@W%0ow?qn#@4O|3;#lP!;f7<+cf`PMHF%G7ouK&Uj4r@dy@Lq~;OAkuZ9Am|Ka z>?hL|K+|QKA^Rj7ijuxIcTz+}PT z3mv$BXhS)l30tlIP@ylM2`e{scmP4`1i85dvI`co)UoFL#HLneaR*FHfkE`O z5rDq;4PSpV{tR0Bx*=W)iB1mct6@LGShxA-;fiOZ9=YJpV!$M(*2hY7NlN&VZVf}VlBa9OzG~&&l^XE7sH31_%6Wi_LYJU4U@g>M%?-rQNEIhbucI|lZL{=BqMJ-`txvjdKT6Q;2m zGzE&M5*^x5;a9>`D{yXvKqw2*kp<{~OSEf4tzQWPOUz6b0ylhE7Z#W$IPL)p4Ke8| zjeQ1_ko`BppJ&~M)_x<_IVizVdmBK{=Fp`GwVr!r%lq*6;)`P8%MtXvlm7 z&g(!5`|pG`&$t{#eka`e;VQlriDDy_4Psfj;re3(;$QNVEQgn)qVI&29;Q{cSFMQJ zhI5G-&;V{5jOes-l-fqvn6p}Gwl4^~vK362`Wu$|&!;76c(Tw22Oo6X&S>CCTcH@>*u)-`TMkJ{PUSi6)38FI9 z2KeH!LjNLB;15DidK>eRK`~%Mzkyz+4dLpQ-UBB{7=)777(I_g3&C=E64t*p0j~3b zK7*|ZeBtVOzcAe}&^)$po<#39WNo^rreJx1N|@IRz*(KN7SN@7Hw6caQ{B89?CZp| z5v(@QR0mv)0iPwzrTCV#iHU2Vy`YDuMnXz59-|uo4K9GVCdrE1sBb$sd(&?t|8~Mc zx)2WSAQe@bRIt)4uN;VTVVE)W3^?0j4L!ldXF&0mBCysJ?~0^4tt4|ut}oC^GJ-fb z7+xkxK4CH!RtOO|fw}m#lH(#UZ2Ef>NW$)(BQr2SEHS?WU@a_B`l1m4nIwy^0Ys&X&{-e8#A8A(b{+rra4)}eL4(WU?;x9>gv*phrP81Y zy|KgcFU;XD-~h$*HbW*CE}EDYd*DFpLUGFbGOYqJESrT5frNzK(FnT?-~nT>QL)yL7mq6*H9%eU>vcf((j-N zz_H~4iuy%F^Ac205J!wxe)9X(U(y%A|B5x0+8{ z@*qCOCrx=P+R-gO>141)*;TYJ=mi9)iX|G@b9(jzGEgOl@jf6F1hG;0PZ4tiTEkKF z4+{%oQ_M1O`RCHYo#?14*^d|g6IH5`7jXp%1ZiRzkE?}iRIw}}8Ion*O`s)CLLpRq zg&>Dwx{m~@-$mE@FFITeOV^hqo&PU%_-Mn@Z6(R&m`+VV_EFo4>*ep7@)J1-$iaiA zfZ+HK_VH_AqXk1BL+DChtmp>l9Q%UAH4RMcG#OUW4guMhx3wMR3P@k6?KKz_ML?1Z zSFRjjFbI|)%2Nn0a41hfI#x;I8WAUs4|fpi6pzxy5CtIB^C&@&U^B~|0SlwaHTL`dcj-(?yNW+jK9kS(~-APpgOrkJswm8 z4>pW}U8Oo{%)7V^y;Ub|dF6S?Sc4p^Tc>=hp)d*xieV%|Y=XCFv{r*m<3-ZQNJxhA zs$Qd5AsNX#_8L7Dk|jF9hw;+h0K!8X@Rb$)A`P9@1RJ+HEdN`RoWs9O#MK(cQ5KRS}Vh{NJ4!E6^U zZPMTRZEp@IQl=5k?oF#3e8tRCCt7aGD!%0-EG?ilNh@>*eW(V_?oN*6l_7MhJ9)a- z3edVl0s})L!zoTBd~)$BJ>(f$)B~DK^+x-8ki!NnRl5E0J{vRB#)Q?N8)dn!dmt!x z;wk#sgB-7C+eOdBim0{Lp!DxadcbN0j`Z)Y!I9`|JdSjfk$EFbS8zSB{wErtOQ!Z3f<<}`lekhO z-#<;suS2hO$+%wO7_#{f!JIz?)^%vP9@&Q%Y=rjfL7SyU=%yYS#nU%J);-B7JZUeK z(v!68m558c{lnm&Mn8ItDtnSvhU4m$)(We8+zi+~1i`w3yW*K466u4jcN!ueeVEcT z>(K&zGOE`;%X+DuJFeDdpgZRH-5W>l)18g1k2C$xS0Fn$+ zl&NCe8r*%V0!ZOa7aV{fg=tyePg;088;cLH5u6Cb=^|unL`b!+iQ@vF=@}My^Tg0lTJe>+-JvU zx(Et{F=ERWT^JOgp5FlL7Crz%Lt_}maTI2X9-;HbWRw(V?Ca>MkJyO~S4;1K8*5Nl z%L*7hS~-srUkc;f`LQ;^!GV%SsGIUWOozd1IjmDimco7lD(YgZ2Ydjb=%mj9oUNS^ zv%A7LR`?P3pl$ocd>M8sA47%*Z^caSH67M zvM{^{qAP#Xng0WO@f&6-Qvn&vJm7&h3@-deOn##eDdKfMeD}Amu#Lv3(nj(Z=A=58 zm%bjovmkrUj)l>sz{`V{+=md;Q`WQeWj;<=IOyqzc!a`SL;)SW=nibH*+CEjzE&BB z@^StBB-9dBvkdU^*e8Xe>mq zW_90!BlGL2IMUbMj3ZIjHXP|}*oIbFk_xE=1i~{WF3IXybs9i$=ai|=f>aGO6<&Qn z>I#TjraBK&w^-OBNMTx-EOS>w{F@8bahYl?K$KioK`8-!ngZb1Lc7$6d~IIq@H&EZC3dT{49TTC~eUng$^M5*-uu2W&EVA&so2q%0E6D znB@d84cO5`|3!q(4j_B-viqUZ0i=o1=W>?o|0<5X@95PpB1EtU4n+7$0L6C_hECzI z0RnUX1sV#&Py5g8!sXeMjrVRq{W;cvo&0u6AA~v(Zy;&GyM7T_4J6I_*Ir^6mAED? zIc%mv!zk|>+8MT5@XH;UcMY6-#BiV%(mi*f#REw_q{0xU8p&8ULT6aXw2=)17vIU9 z$d4hs23=bK$7D^620vVvztQY0iv8V#RQB(h4O=!J^~nL z@cIU0I!P}LjDhDpB0A?58p|b(G+96u_?8Um)LSTyOZIiUI1l^MR{$lv)L+|;ZQ&7s zL2DF~=RR=-fM-JSYQR%0mC_YU1?!0ERrAn8F1c0OJeOhO*E(^iqF7C;l?th$BVl7I z3#oy!LG&HK0yFS-tj`BS#)PrKvTjsB13jY*$O0Oy3S&K~TQQvPgSLW^NIX2X@Hs1p zJtgpziiI|VFB@aJ+%ho>S*~*mXcg4NBl`^e-$hT)XGtiP|5;LJA+lE?hvUb3^Ht!u z+Iv1arb1eaJ}Mi%mm;W8K5bfxUaFAh!$-w{K^4Ocw?Mg;1=B+T0`0Lc_;XEMrivZN z=XWbd>Vf1yUdvW=5=@nQ7~L62j_Eajt5O*Sak@b`yHz8YF(!P(@A&~Ge{NO8Cf|Gdu9DD=|rlTeIG8EJ8v1pww>EpcQ0G=8_=)eaq_60Dx$uzOdaI>U~rRLg^R=yR0Q%-L8AteHhl&RK0 zs^3AJs)dw*h1Ej})6#3=z=?<7EoJ}@Fip%cw}yw7*paWe>hg*98Tm7VlbR zYnnbE1%te@A|1^dLOSWbpU(O{U}B5NXaENAn6H6{E({?J{pNuUCRJ%Ahk!gTx{DyE zl{yB}FdrtmK73mTv%1!LCk$|X*s9D8fSf&(8qoj-pN`81Gdefl)*)R7vj4z;@-Xc2 zb%s{bBbd>FR2I^}Qd1!nT?J-4kjBQ!D)(a=*UGN?QXwbfSGLxnbO+K={U4sF!2zaW zfd6nHL;C&$|5H~9G#}R$Py=;f28$ixR13l#b0ls5Far2*svJu8(`2y@P$r?MzGn9@(yIpxnG2BAb(kKPLZQP*ir1KjUJQdH z+>Jb>>O{`rPCyHtNLOx8bjFFC#LK^e1kU6dt{d9m49Dg@_2`*1X{+_{G(Lm}OE_?C zL_a=-jE0jFxdCX|aMBmk6by$WRihle7*2W*SbY+#LGVy|i_lxt zcjwY&JgSVl$|xPmiCsu9-mjBrmka5{YvQ4ME~E|S$GDKa%$5NkC=wXhRc-kRcYoGzWDCK;69Ob%_YcbNvjWod(gu9UrrtH$` zDGUt_QeO9cIgS|z2L~yO`Jyv!q!ljpw;Sor-GGeU$zcO=p;#^K#Klji=|Q-R8ruZ{ z_`IU@S+eReM^Y$Y$QIC}yoF`n0$k+xvRiZv-FGMZardF`?&Lu|Octjn^S6$+FLy4C z1AiV~ve4-fq=8;0Ono69RC9C&PDQ}BOhXmKdNP7s$Bjl29;7wb8KroD4@d1H?o_?L z;N5_^{uXwRc~^j^S4=;@0BeGG2?z+jEtzJ09s1%yP9<1_Ohe%#Nr~o5n1CWD;U`d< zz`b%LIZ$s;29)w*&naGfMHJq4y4BR7Zl0up<|-h^)Du~92T#(1I}APgm_)_@^9moDW|rOuC*`v2(`Q#eRWf?vnhBbgVR@SmrnOJ3x3_06#3pQwbJ zj3NheM@&Lz?U@QeLRLX z`I6(f2B^-LoYpOu-S%}cJB`MSAp?w94Tb{(R4bcmt{CyBfuHuFi(|+^+zY5-40&Iv zYIl_5M~;R69iie+I&fvk$sfF&vyZdArm4pEnzFyVP`W>jd>li8%qwx@vO0AxabD-6CN+{n`RG7F>Q}3#*zly z-RRX=(i#)#1dvNizCvdxG~t)gaxcqZ*mtq}M$zcRVrv(-raF`v0IM3%mIaU|`n#1U zz{FKTPOJz&>tM~jZ8!QAKn^h44`(-}c=(QS7JJQ*Zv)K`)A7h_9Jxx}6t2t^>avSQ zZRplG(!9Gnl$aO}hj6ikRp$3ysB;`SM7Icp2%iM=B=}%pc3cg6T6!q*8Bdz^D+arS zy{VF3suWf>T67{lwufxAfed9#L)QR$VyE1bEyN( zVB26vvO^aa_Sezy?gXyvlm0t zU{AsHezY|ZEO#TS4H>({YFlB8|BRkwp;Mm-`nQ6GU2Z#b{{|ID~#3 zLJ9t45A-C66!Pl3GCy(VYFEZ6nC!z{k46NOo;qh>e&NL0rnQGNx_HCIUXt$$CS7=+ z4x*}HawK;e(wRV-x%z;?|85ez^o$j_7-s?fYNfRGQ`x4olIP7})3Ae9WB1^TMxwB0qrmd#ldwZwKt+?;u{BFD~I z5VykC#s<9zCuhPf!+1Mj3PIam#BA+~-$DEY!U=#KA1q>;A?|<>2x%N+*uTTx!Sa-e zFF>(Q`IlhOy#^XIA6rP zxAho4L3~5REQAnTAY!iO!9spj#F*uam?_i$sr}(~5hDbeR}d;7bZP2O^ePkHHy?$C z9)d1}ZQ5d!bb$RPz?0x_1%t&;PXP}h%BBme2Ef8y*5HZ!0MNBxP2SL!C0`olNd7!T<=!?M+-`OFOG6jJ63c)M_ zVo;h}wwTKx4R>2^bga_-d0`JS63~aIqf_Y2`F@M~OHaNjNzRyrNQap>!^>*PgSpY;ym5sM!!$j zyOl<6TCFzYYeA8*=lUOq`mMD-ygvCxZr^o*7CS3EpV(e5z4X|Da)RM)jHT@<< zReC4AKmTnn=k?T9|L<+#&t*rmu0D3qYro@BBsoP!PKw%pbJ5#bB){>BS6t1jhwI1j zxW_kEoD#?@PN_L?HTVBZOWnG(bVd!dbaI~Qq0>(T`mB%~(&e6zk2`g)@%kcT!DRoD z&k}_n7HtTKp?CIe{M6zx^ywVc%S98)=jB^;3)rtSGt?k9rr^Y7@_lBE%9-YfpVQ_i zq&#-lIT^p{!03&4iKwq{0=~*FO!k^?Q-5=gR%LZbnt0s_<4w7*)3$s)5jpWeN{93N z!A{cc-yfXp$m*;d%DXz`!Ib<>n49No3Q?Z@8_cXuIfSO zZuZFO%#bvW+vc?N+K?i80e{Ju>}h+#=b9atJ#$>&ZBM*d*P&RTKVwOV^Q$Mdbv~o^ z>g{;BrDOD!UB$h7Cx|bW4{1HHn!aP5ddu&td;a~3#k|(#8!OJKK~RUF0YL~s6GAr# zS`b7Kv>|kd&;x=F1YHPv@``gkL9=Q92hC>n1kDbt@tEuMVsX^v``7FnMR#X$(yTh2 zg6<`b8+E&4X0yrOqH=4!oW4!ZjE0=KcE$Yf8AW8Z(a1A?y^^X1$iMBK{qd2tzhZXI z@tenzUq{=!JwJV1bdEzrX#5&<;BbGRLa$*xm#vwU?R>UpbY*7y>&6L&ZTm-6@((%m z*!AR$Yu00#bfim})BKdpzY2RFFpb~zI5NmW&G^gc@YPGyrsrqgJoVhS=u)~`AAa^{ ziPhzEz2jzm%(E&tb-o$sAiSKc?msMa0@3aLyZi4xF0r2dc~-=PXR-T_oEZ3_CL!&u zx6b(Dr-yzx@5@Uuf8^G>#`ene9g30a=0mf$T(yq3-#<-zf^=T53V_`zRi#t6gjuBEc{f$tVB*$=Fy)Ux-A;& zE*mj6>|1!?kHnKF#T$dInDZCRRIa#&UHbOxh_CGAp1`7-nT!0lesZ}ZU8HwD>dss7 z^NsJ;c{Qp3Wg2^V>bnm|>I|E|yqln6_-hQ(-XfHW& z_zagu{Rn?+(Ijq1*{PL1xMO@eU^&>0`*Nq9v z*IRsH#poNF*;zdoMBDnVJ3FHP_l3K8I+VO32dn4cf7O$V)uUMcWao#Yo`IvID*PU; zouRpVnn7xqUM>B3;&6BSXE`qxoS$5+cQCwRN&SG}ui+CnE|%#ncTxCg7hO-jvC-dm z`q9_`*ENe8BF5T0S|Zw@_)^e#X7JhXXlm`}xf=c4BndZWPp@zrv-yy1ZpWh(gXguU zr57rEqL=U;$Ii=CU3o(4f2?mSq2Q4@D_6LrwgPJH;K-VK|+l^YLjEW6algLhC@UXf?u zFi`!_f7SB}tLM!Q#n@lf{;}7cE{0#dx{>z&II8wozfG+x+%=B{?@7H_bgA#e?7JCj z?DSf6{<`+&>Blci<2Tp#u{l%iR5AFD=j1@$2)X-~kOS)@V^eP!efp+v;8f?JetF-j z&I2E{Dt?%~JhZ7t(Fa%iRGYM-$$LAv8z&~U-Lm5a7=65zcfYrqwCV#X)t=Zi;pO(Z zS=T4^yEUjj)VZg{sz>h4_nZbL*u@x>7u{Iwduiw10GHTY?(~z>EOqyNIGX=gWq5I; z&0h_JOp>}+Uzxo4Wb?e|YR{~ke^r^j4v$oi+!nEL@x(i0tB2f4G`qZesxj8ms5aQ>%QrA2b0nZFYqQm zF`Bc!s^ZlF?x$ntq^d8jS$5Kg#v7Du7<)8f&(J3qZ{BcYst?=jneZTBr*`O-eHuAW z?YvKwRDVxsND)oy)RlSkIpFMb{HgJb zs~%6Ro{`~a-KLn@KCs^W`PS$Ry_L7hOU#HhlE+hzZ|1)!H97IJ)BS~oy62+QsVBFX zsk)zIGD`B_jeXLRRlWOVpMyFDD|bIK+xU*i(!KJv@3|9?td15;9@2gLyne;;mgyZQ zm+g7HVYYv$_J!pKug%$;tQzh%v*rBDXJt8+PBRboiZBh9Cg>jPK^R>YbsFfJd>@i~ z?X(v!?_QAQOFxH1w~})=&Tff5q8nXT?;fLed}OTn=gVWyCvKi)S5&-Cpzd4NpZc&l zDpw}*PClM*cV*q)OVaB*d>*Vh7Cosm&~(f9<2_0jpFOnAnW$O(_{HQFr%Za%vul@K z7w%Kt^E2p(0eaik8wCK_Z$%{iR zWm88!6F$4LZ{Nbe)p~C}Tl7No@l(@67Cf&WFnCm?`L*+0mG6_TW_RYle zw=U0UlSw%_N%O*D{dzyU5!+n)+`gYtOW13#?!)HDFJ??*eY?4$XW5xGj zmy9>nz6;SBH@|gzMb^u{soQNm;?_hRmv24c*M5JGNpGKD#Jw$vwkK_m4U>z~n+h~H z{hiP+yn(Z_*|+%9TlJMM{NI%<(kQ!iZg=b~fAg&M4}>3i%f#}EC0IR~|5eXHtezc{ zcSQ9061nZ{itOyW~%;}!_z_+Gv&jHK2e;HCXbNTT_O9n^mow)IC`cl2odLyps z3vb!l-Y`mA%Q;|mUMHi z&qsp(efrg@um8C1yRahXg5N`(0MmFbGed(Ib?3|S|aOsl`z^i=kOzgqs}bhtx% z!p(_2Bu71ZN)P`%{PVzpk}#c}gQx9WbFcD+N4@{17PD^!kz=OVH+*=vZ-c4+vEUX5 zY0?EB>*ud~ymE0HSfiI8JfvHI>XW_MV=}&Vk3Ija?o{h%S$xyfPlmGshqu~qav#03 zBx^eV^R&N4`SqR<_TEj9jdK3>Kc88&XvVMdZ96_}j;~DEk)M)wFi8EyqNK=%9RJw0 z7cCY>_`Wpiml002e45t#tWDK(&b38*@*nAV_$}hC8X>Q^i`8@Nzv^kn>M?QcTR8Wg z+V-@li292+-wI5pH?NE6Bygv?rWYLW9l>}akw?PuQy&cyX#fCZ-&#apUuZk z_ifI~uHsJI^1S~;BVOUAHr2hg7DKJZsI)Bk{8at#(zWKo%nP}h8+%Q8^YwP8nNdpI zp1X7E<^9K3TWG4xttfEJ9seZz@6*zeKF9yos~jH_akJyjypMxc_ewQt^KMUPWX?VO z-kU9)`^IvEb4Nk9i5<^hNUT2JJJ7ALm#+1w1{=$-W|1=j`y<~=wbJqQ%j`|gALo{@Y%(b|xgNQ6)~NBBBClV( zNhNz|5cA=FHq0B!QY}q7UL@RICsvSJ@{Pm3Tkll*I9mFcfHuH7}>6$b0GRQ zD*txzW?heE_tfTjj5)tE?9r;%gO;E5iC=tbU~<-*fe8xp=Aqml`^KbCn2>h#a=v5h zPVE8xha^ALTUd5y!r8POm708=ncqkFBt2PiH{o8%%z_x(1)tw04w^G6_-qer(OWky!QFh0WCwJEIoA3Nu zs99fmN%NG!V*Axg7nh1UhDO~x%#p&9ZB^54k8mPflTxE@ z&U3sgm9Kc+(LUuw&pY))%%<~oCg;uPt@M*u4Cv)BQ1iqe_55?b4eeFHm45kkX3bB7 zls-w5b@Nw#U+G*metXf0acjr#^I#IH`^@@2r)G@zmQCXG64!B2msTAt&K=+V_VZ=Y z#d*UvMzCDje|f=ky);?2O{aG7 zfVaFH!;KB$y&xFL8^Vq6AJjbgzcqFLyD_$WOC{H2D6XxEeA@Ar`zz~Tkn0di92ZYW!w3p z-b2j$WjAW(pWAh6%tYqy@S)+BPhRDgZ|-a#^X0wj&O1S()QC0HQr#KL9pBE_PK|Sa z2L`$cNy65~kQx&!@zseqH?>byAJfhfK)bva$y~pd^ym58Av3zyANzGu*Q~z5_ ziw5-uEfMyYcklaj(oJ}G@Sf}ej{?s)pAA`RZaUJ!xN^?ZG>asI)0a!lR=j_EFlu9K z9p5o$rpD@@b7mD?)tUDt;X(D_wUZ6QXS!@pi@CmjO6&vg^IL_Zvj)9gX6_j6&P?xJ z`}3Dcq4?9egXKL!Z`>OQ8j_K*uL%8QE2DPS6x~nFTrd##>dG^kk;YaW8HQ*FE z92J|nxg`auwxw>+8h3ii|F>z+c0WI2`*-UsuVWu3oiAD2srYO5;Zy%a-si9hUEb}b zb@8I0GylRB<#Qt+hq{+nor*9D&|COJP@yxsO-=0I@0%s{ye(5NiOjCJ<`@ zu{IFv0I@C*>jAMo5E}roArKn@u`v*v0I?|$n*p&o5L*DTB@kNyvGsNt1DhwOxmLqt z1==_MV*`wm?KR6MvHrMiY`TQ2aQ@0gw>!9J2!zfpyzss2w5`j+2WkwAm-lUWeR8+_ z9ygb(2i5a8r~SHYvvo>Xl2^77(~6?^PbVh0XFjnNe*HYQbiZ|h?0wcd>spM~-#NSS zmhHRT{RJM^zFxkpzvEi>ipARlQVXA6-MweqRSxS&)(ys)jQ z&up8*B#q=FydEXQ9H|d{7`u0x&qA^2(p^);RfKBUTSVsn9<6S`)Ps3=!*~cm2=(U3ASSTKC}JIKN}#n1!BAHXa3m-Fp8YJ*ZllX+SwC!WefZ3 zR=Ep_lJ{%0xj60kN{_A6)%h0-S?goPb3r F0{~oaCPe@M delta 24472 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�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 W5jYT4qrxmjPDD^cQ$#`l0002f&;U&U diff --git a/device-software/utils/flash/ifwi/edison/edison_ifwi-dbg-02.bin b/device-software/utils/flash/ifwi/edison/edison_ifwi-dbg-02.bin index 653640cda0c6611fca813ee73b383be4350d5c03..29523b6cb0ed96d34c141570f2920ac1e523f0c5 100644 GIT binary patch delta 24875 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

    0}tb7M4~Vi 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 bu1KOpkr6l$Fop*!MNULeLsLXT00000+6>qZ diff --git a/device-software/utils/flash/ifwi/edison/edison_ifwi-dbg-03.bin b/device-software/utils/flash/ifwi/edison/edison_ifwi-dbg-03.bin index 2f04c3e0057193240a98a00bda3f05706f7c6911..46c68166945af6b339af815dab7b21463b529019 100644 GIT binary patch delta 24874 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 delta 24471 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 OPDD^cQ$#`l0002kjmd-n diff --git a/device-software/utils/flash/ifwi/edison/edison_ifwi-dbg-04.bin b/device-software/utils/flash/ifwi/edison/edison_ifwi-dbg-04.bin index fdb4bffd12739a03afe23acab0e219a1f6038c84..8c6fbe690493b5ee596b57b182f3edcbbbb90bff 100644 GIT binary patch delta 24875 zcmce;dss|e^f*3qn$DSOG82`q6j6~bREi>{5HXc5BKMNqY1}hqgkwamo7XkD{_}mFU(d78-g~XJ*Is+=wQp0ojaaS%e1 zzU=Px(O1e&@LE|OkB2@H{0nwJ29b$;)2@mS+Tb9x?zcxcOqcCKhVy63r1wBtM>T;qo zVs`l&_b-u-MzZDiV{dr0)6?D+cje7rRzi!>&c#*hEqB(*jy}DAVT69VFg>bj_VVN) z3;D<0r^dbLF(}ul$Dqfn-qIV7cYFQ$h1asN$m*6JbwTgI+qD(E^ZFUvyDrwd=9i?L z^XA-&PeeuSipn&PX@Lv3Tt8x$^e|~d^x>=XhvgkV`y{*1zL0vUPx9Gx_3HiK&wroD z8@DXrPu?=bf558luO4T|=zrfdj~S-3 z%4l)Vb3I=6T3O!zd)FCW#3y^>24lAN9?vKCT}Iuh-fl&g;_q5_8nzgf81pUo%Tb*% zf0VYpy-Ai(<=v4_@wN3W-wSx#4ESADGfnurDfBf%Xri*R;#a@WU-B;%|29SfjB&I{ z!01Btu5o=@jz=!u!?<-`)3qsm)rQ2&E9Qi4uvsu}V~9BqKVBbD1N&B3BeH_@b_~D*`%SYf($ns2)Ww4jyuKK{zt+y``TN5Y#%~wLZ`!%^lSje$XxqZ2 zt?gP1Cz)Mxbb5GRzAMwwdH!E>re8ZWCTE;_>KnqZ7vq#aTPDnz>DcMoiVdafdt}M% zf7#Co`eKn~G{Yo9Ffnk-Xx*IJmFJ^I)MnFpYrXfJU9xcxeckW2YtH!4ywvlvrv#r4 z4)Zk?J>EJmXyqfDg}b8meAD?k?+JA@?An0fiRVk!eER$8gU)N#*Y+hGcdaSet_4A$ zD%q~>`crq=f7O&mCh-N>5H}eZ%17=}baCm_&=N{c;3=(<43F6Z@p7ll!`pRl-)uls z-Tmxh6BM@L#>>T3Z)ow{8~u3x_AA#;{BTy9I3xOtahKwbg^l-q^*G}+dT_=#omsW3 zLKENBubkHXQ4}?1Uj4EYR~eJJx)udFj@*`ODj{w7Gio!5ri@&r6yTwJ+?jl=l39=brZ-$r$4D_KUkHvk!%^SIS9-A(t>rvBZQY(`sg`iw2osS)|OQPCQ<7BwO7Ccytnxwt9^~ryOcX z>(4AlFA&*-0EFHS(pq?P0FO5&8*;8Y$ao{?Av~UK&w|0aGPmHTX8Ps@+cVihyXOP{|&$LVj;JW+7pIYV&xEAv7>V%DM@8(3;wOXifMi z4xREnp}nGB6co5g zjLsMh)ETS54lpErYq=GxG}?i``a8>y*wxs#`M}2{iHm*^#QjN;o|n%{)L3;EzSe50Jjmze|a|xkBQ9 z6*6Ge$8cYDD}-VKP4}vID5G0I-gH5=6-(DK?Y8x z7mPCQ<9D}1b2#@rh{I7oJs z6W*NR{2|zH!p8A@fS}In55kHoDFTEP5T*YhG%QPjb1j1mm@&;PV1T8mI@| zJ49Mp^o0yq7N@REfh?FgK>(mpkT8TffOHR&Hk~$$I0^-;ouD;JrVl+EPcR>0DB!Vl z1C2OLniH=a(e%Tlr#k`j(U{2iL7~h@NGS?MtjvOy*@QC5Fxde;O{vWlY9FaIJe1i8 zH7osXil`7&ahSA{t1$`VLxVlnr2tN2+lt14i^%A@^Z!C_(ICS^l`I`E-Fy zUu;zx94w6+31+A6rf4N(y4VU=v;+iJ8rk8zgc~yvN#25L3Cjx)P3WT@p5nR(vq)&~eH}O~(o6L+puJK= zK;Sco|6TFZO7;vb%OoxMxo8JO@?Jn(5Vqfu;}87yJN-_jJO4>}a4BV&P|?IsFi!V^ ztfGlX5T(09Dy1wzOK}T^b69M8zkd^&#HJ5uqtuU0cmE%xN+TbRGzjJ|Xh*()2aO!8 zDg7_V(EmWvM{oeB%O#hty#!XlivY3czrP%dO&?i?l8%BkO3Tomqof%zx(r=9N_zL$ z3lqM&oix>77|)l2@gf)nxXb;8+zg86oO~nboq9iHb&TvH_ZQvd@d2yNBR-4sss&0Q zl_bBy=Q&!EZEq~WZPm=Ye@J+35M`L?H>|CDfDs4ebG+y@0gO|wd?*O643Z@0<7(*Q zyjOy*K_Vsbqt8R@3Y}XtF_xo(sfdp!B|eV@bH53@Di~Ir+#E2T1D{4ukCC48V3#<*UbhTw*63Lw2+DZpM;=cww2{bBgaP9`L&Ts zF=_Arklw+<{vkEdkm50zDTkKAOzi5s;B zbbvFMiPDfOpy8236G{P>$JX7J^5OIT5Oo7nkOR@b3W1LQ$pd~M`#+)ci`YW`r#iVVsZn~hbxD3BC_b!4Im8v4 zG0QP#bxP?Wz^VEV)hUjuS~~HkC!D(7$NVJ zB}s0zvf%L&e6mEztw|t$f_s)pa%*mTnBwfR1OS^0ur6}lEHb&32b(W-`z$R;Z3n0* zi-6QiND*0lrAT8N2FWm%cuBNDTfeXUVyVo+!RJfNokZ`YY4HH&bZI@*ptfmK%38il zuFLJ#pw|IOiLZHb*k(WiTAT!9$QyKwecBQF4C@F17&11Xm>Ep3j3BghZ}yaPv)-tq%B4KT|ABm<|5w1>V%6V8!A&YE)K z(|MYr8hWm%hI~A(U8CL!v_6!oI!8K(4h2x?(9n42IFsH68kWpu7j8DJ`@%H_OP0I^!9)6%eFJVB+IODp zsE1)%K^3$m0P@b0Mi##X0u^wLSNhk`-dHElHuU*C*-e1uL%DvG5pupjb|KQL(C7i zPoom%kvVR(N!-HmfO0OeLd)C5e?%uNC->! z?ssUkitMI84nz`fJO%~ec!*Z3NINfA7|PHfrm7W>XxRYT4Il5sRuv$tl$rmA!`^4U z!^i~j`oapDQYn)FGS2oUdZ&U_@rq{4k@8(lF&BXhxctU<%_dzf7~sciz-xV24j$ih zEzB(IU=wD^P-sCmX=jQl)&&V4Rx8)#62RbA!gsug1NwY6d3D%w=q9GGD@>i;HK$rQ z91H*F`BoAVB8>~Z33Ow6bM`Bh#!ZCA7HP-97b@kkZuJz(xkLsL5bNZSeY){HFCO4c6Cj{ z4PC5}$R!u_h4<7hW$KQ~wH}qQ!_Rd{dMQj0(nf26P_65$$abzot1pwCr_6;`D%~&` zOh*T{VyRNt5@Kj-I3zFv@5%*_*5fK#Ku0rfIw*tiL5p7N1-z9(hV)toE&&^_5s-jg z6?{|E>@YCNqY??PkV8A+^1K8+PuFK@kdn1m_g4zO;d=TDz9p2Q*;hylJ51w4YrwRI z5NiO7#n@}QM=5x={xv##h3syXSp!pEF5a!f0n+jyoHiRQ7yTFGje2f~ekP$>? z4GO+W`Ux?f1}fF=tE8A94%VQP*GW$tGuO%VQE4Cv^rJkGYd_AZebcvcKwY^T2gEsb zuk;-p&{S^40dY=klD?Y*TFLb|;O;V1kxMQzcD^NHJ;SP(->=I9`xRddtLeAS`4)=F zBMXVDB2~|PGJ;Pu7NJ!I;Ro zXT_(-I%q`*iu~*vJkWqb(nS2bNHuuE;u<_uF@A&*!@<{kcOmQ`jj&Hx86*^|p86^Bg?Y_d zu`~yxMb8>Jln7ESkP`DbIE)%86H|!hKOj#M;xd(W8QGCPbZ0(vT{C_2fm-nv9LYcg zE{&n!Yh(r&z@AC`FTfxGq%LN2U~XsWpWu6am~U;Up^eB#Ne@X2!Yv=|eMp+%AKLU6FsMpcl>s}|AT6_Cg%g-=LG+%1UWwOONFOrtcy1;EZ?Y1sB_fpjSG z5jl{UT8^$gB0HIjfG9a1e`(lggjKt+YFlMcH53z~ACJghM%7T8m1_|vD{X|uj-@Se zGhk`r1Q%ez(eb}YH_t*S#FKd|^=sfj3KPE5jC{l7>`ZV*fm@lksB=*gPGfIqQBqc0 zDz+#o`vEeoAdh-Z1ZC#E6J?YDcR0s@GHCKQM zOe{yHPsky`xKCOWx`O?r`teye-1D&+xF4DDW^M$>7HQ)wk2n1x?jz`H+&9rrW9di} zrGGVj7B`*_D`nm_!Bz>_o_PWtAQGa_Pe@yjOhCpe{h$6<76)eUWjs0f zx5%%tM;IJjO9D$@=Mkxms{ z*Z^C%Dsl;qv#P+GzFUZ1R*^15rVtrEBl}@BNuQDSdRT{AVbydRAXYsihhU?-`;4?9 zz*xRKBRvN`1fLU_`R`MF4v&Xtwrt$lptEkg1{RFLSaFMjz3w^zjF5IT*f_{5*v69> zuxQ3}a)K@%GQb26*Yf9NfBPJ;DrHVKd;`R)7xvHl%Y^fNwGzP}Cd_*Y$6gM_Uzha& zdAuOIVws{}kdC-9sV~S?T`QpALL(TR5a)dq#U{UHVRG}XhAcB54qvz)*J`q>{t2j; z!zSlJgQr%LbFle6swUmKVKpj!OvI{+>0hvx!G+m>JJ!Ym^BI-B_)nFF+n|WkOLBVX zE8r_ueU9}s!Zj?C2IBFDZP5s0Qp4H2m&Wv$yjLQfJ4vDoe9#P$_gVzA2$ts#7;`!% z&#N}hX%91y4(>vveo1yFP6(0c6=@Dv`QT8Zw_|CUnLwXo40M=k;49J@_r$zcU@0K^ z>R05z4tP39e%%V=PN!I>nYOq9a#p+|okX0y)hc}qGvF4k!)tP$W0AiI`$+NSOPxCy zE=-(oUIkAgc%CNYaUF|&Vjo};$c`&VXJ3;JROuJkD_#snwXaD>Ga=Lk3nJ{Ixs{1K z?xxDVV%d7tkk&*+C6d;Vrg~LRxXDoA4OfE+HDn)rVR*C#hT~TXy{jQt;{5Ddawm>; z-jJa<9{+~yNX$Bo7QG>-i8J|hIDF~JOocUxD*$Hr-ABl$Wx@nK zjZxP%o`iLa^HSASLlTq}8oaw{EI2%&B)GS2G&KbZJckOFca&-vb^wMUq0AL104&uy zKMKIx#Y^BfYi_#G0EQ&>QZW`UtJ_YZQ4DE>TNKNXgXE!;LCM_pZ4IcK5rPMEhEWad zeI1gA3NfRMuTVF=5^svLSrv~n;2aJeFFEFAO3oqg#~>d`N0+AhmqvjfpIgrcyd$M@ESYCP zLObX%oO&pmmc>^SF1g~1xwO{}j8(gPa$?FH&tgp3+OS+kAG5C)tqTLXGAV8Gh-7U@!+my}`7sTpBObYyfbd>w0SAGDu%dnzdUF7v145WuY#&m6mI( zkeixxk*^0>x>Y00b^12&g{4-abQ6K|!z%jqV^Um^mDO`D%!fMF*MHqYNF7W-O zCqd!7Y63j`Zgx;O9=o*%g}{Q&x+MULdBClbR*W;;#if4*Ip9<&)R-|_&BdrzO&Sp8 z<)~3j`r~+DJ-H6Y#r32+j1$18HF`ge>;a;TZEM(iXL))a{g78KOp1)j$sDg@NE6 zP(m+;GkEV06|hDX&<<#RHlDVy;~B8vZf-NWLXF>wm!ntv+& zH*si@wxEqBeKB-{(sET5>hu|WbG$~C=VvmSZ-x1SKT<&R)!HB{>=}H8Fj=tpLI>_2 z8qvisq?7nRROss$(%xGa9zZaLLGXDO!QIfTwqNahJ*8&q$a0qjCAfWGm z$Cu$?6MV*&vjYh;4Mlw=L*x!n;g3&vNYC&WilVh~{`b*7A{>Tz=!`oe$r$OYXmXa7|Mqj@OS1KFdSU9JVmQMU<8(EoLFKS=YA%3) z=9wHLZvBhsiiU`DFla85++pGAuRXH~mQpj9GO=o%E0%@(D)L3iO+GOF$(RgG!13j_ zkRq6#{NXG!*HG!9RB+Pg!{!r<{8`pDuUa>cFZbS{=NK-q^Oc@3@d{`IVFiUXU0~Q+6sjW2j8AKtM z@8od8p%Jb4POfm{O8E&m`b>f5d5Y<6DD&zorxmqnCO^R}laLv};}tz+fY5U#Na6B> zbRaBmqX|Dq@9wyY@A;C137Q6Rth{mk2|-D(2o1}Aw^9BN(%u-;YTB!lPjA9GMFup0 z-v}dm_idEgNIKeZTB&u(jkuHsCQSbgOZ}JQal7$kVE_(3=(eq~=tU#hMVbwQszqLl zxbs|GE*AUsXO<0s-V6>7k9%AMfHDB2kB;UxZYjBotwZ7^aTUNzN?X=IRHR)EUp!Wr zUla=dNgB)VU_MGH25gvj(94V&T)i?o-~wKWk;A#S2 zxNgobOxG7QkL{a?Grj{^$2O{QSRSAf<(LCFtCbN0U8;XgXs9&R+rQeSQp#AsY6DGm z#l=|gS;Ab3Z%J!dSvBJaJv@E_q~bny1BZ5y zioThWyTm%@HV|jSFk_j?aJI!7dWwrrhT?VkV6E%?OHwMuDK?N?l`BrMfH*W1UM9tT z#$^3DAtc}g=Hu5&Zgat~nIAPE3AcNmkbwcxIGbVsi?KxMa|Z$>N|{s+j9>|n(*C>S zJTdwc$glL@4U!G@W=9b`@tVCb%_*Zx85yi*t8x>Ju%bs5Bgba4|G3v0r9l%qV~6EZ z%wZ{TfZ{nDAd?LjO>CVDaA1s}IPHI)(SjJ3&Ae)1FCMRT4(*?%)@F8q7;t@_883mg z2lo?0Yoi;@WUqGc@KocRTw=rwC$_hcoeWOmTFO6iwWtkqa*L5u3tXbrY953Dmm>cx z9>GughuNJrCf~&=NnlRUf^@7A?Fy*&GM>fbwYjG`tZa8YEGK;c1iK-1L{1%U1I&5!gMI;`HqZWov91{V33@afW8uk%6a(Ia z%N=xsRTzQu&$v9S9@ySN4p@hN+kD$tXj6wyTMH`1tKLGDz<_nbmdd<`CIH8}2Pozj z8A~K?^<)xDC57Zru#c27r~B zurfndhJ8TzJ-}64P8ZxX8D00_9akW_E*mG(6Pi$@fU*!S24&JlkXtUG>4G>t>^VL85t(XJeTk0O4?c=r z)$FI+3jD^%G3q1UrT%_lQ7>dntf0s7iDLg{4#5Qd25f8t9K6GIq?B3F1{epI!(5SB zZGbfnZGdqAfT`i9jc2<9E=R5 zFMdO%$Q6oD`wGDSC1oOTr7&Om!441HSu#V6zsH6$6Yij3U248=5~v0qY*+%jySkJm zads1`*QK0@+d0Tmj~ZcAsd=lRwg?G{XX8ZJ1aC2Dg&wt=n7|+l5fx69y+sKkY65Zi zEqWxP3JpW|Ti8&DxWCWi@e2@Gx2u~nj@zA;P{}J zO6%&dj60$zN9zh>=};@|0WWQV+sQbLfLB6{e-Vn1P)kJvE?}=PhO}g%XA;Ur)D9pM zqA;W+3+WqBokg$DvuxBD3`x&I9tPB4V(~q+!hkX%Hd&z^1|a#`a@1fz4R@&T%;QZ^ z>P2#U(}&eVrR)L^MrFk*zU~t&Eugiy2IvlEUpboAo*G0HA#|)gb=-V0Xgw|t21c9` zr+9balZ{vDKF`tI4$x$}Gy1Co)wjn2joTmZvoSLRO!yFVqbb*R4+P~hpP}Xs)F@-8 zHhMNeLa%TLkBH}oGSO$yL_?~F$l)GnsepiYCwBMHc0XFtI=m9jS4I+#IdzNZFYyaEbaq z4E|~K<0t5DN6Ox8WR=ERVRe^HhTTI5tSh)Ho|_?w3D|n88456gDNVl$%{HN8%>Tj+ zAHejb5jpFRL>DU12NTNQ%p|dpyxp#q*e+cTtcj)kDVw^rmN+BD{x^ zOsUR<$vu>AN_BA*0vNx)knrmAL0sHnPcd)3KUMuB``N$ICsWF%mlIq#z(`Yu<^JyD z&$!5$|3e>7`WyM0Q9TI#r>faz)Es_@;1>)+IQg`It;5~`)-xVJ)`Ap8+8CFJyH8sP zDZJ@|0}!MzE$92$9ofXi(mh-RCjx1@1UXqyA;ivNq_Chu``D?sGZqdjtnd*7oy@08f-~<^ch{sjn8xm6o_D@c6CND zC_p{G0S2dFnYunA1hvo zl9~hwj-jE!aj&6n+J8S20k7q-P9b>{_7hN18(U-G0|<31a|+;G?W~mB70Ni_2i}Le zO*N%~=tL)=8Y=7j4}-^#I6=l?y3b|kOebon1E(B#735!&ffbVKUs2>=0h01E{RGu5>-$GVq>HQm5i{*NjH%aMx})k(Am zndeB*I_OG0)TEykhJ}b&bz70$Jq?D8P+G^Zow@tWq zWMEdB{r}!{4h=@rKQuIsQ=3%C&V|cxtWD;w=8%cE>oQysyvYFY$7}Cf;~u5}{;kG| z?Na$$h)_R`A{5QegJo)f!a@4R!Qb<^0#+8#QU=xlzEuCNnN074pmFa&$8ps-_0QO} zEJdrWsT{fGI@mp{a_Im=Wgl3RaGx?RzhNdEj5En^a4O~KEdGJ%{HC+wV0aJ2-2F{w z^AGIhZ)%7lvglND2-fvxD8;#dyEL1OTC|y30z6!nXMs=K) z0HaHdmj|&bfRr-hR&n%20UlU5=;=pzgu+}z106HB7`E2jAP56rtCB|tc>R8Iu@~c| zfCo8%WQ7-B#=T=Whv6Q@XhwHRK4h(CZ%b>brz(xUfQ0 z*jz|qT9_=emqYxU3)XRwb^<^&ToyyBo-2W=G+dG(br-`Z?NyLWXhrq*R9{j3F3vy+ zaD^h|)PpiMxN?V6z8XKv9}>=Vvqj-OsO~OfHDIM6CG7?07u5R4M+38*5T*fV#{FMJ z=wuJ7BXOZSy4i#3Wbx%T$Mt^|$KH4J<`)?z+yMt7d?kRAT1hjHNZ0^@x&HzUMc}9X zC${18+{wm&JD~m?Yrsx^JEiwR9f;^jbtA5wMfN=@Yuk!*EUP7}5vyP`6&^wRmouKQ z)q-E{DE-Uf+#`hpwTS7s4bAIG86z#0w9;FPbtCeGmCOJ+vT*UewiN}kq+hSAv*DPm zkI~?VYx6gHK1i^D_ftIZ=-*B-h8!LoQfB*Xl*58I8WYp&@K?%AgW@16Gj=xm#FCwe zfwQ+6P!c6!I)OcPY+|@ zd5?s-cngi-Qx^IhpbUIVW=!fWB;!+Ez0c0XzVyXF2`}|mwBuTM0ASD>_1GCty#U~w zT(BJQG)JXz#nPdMQfApq^oUQT$!lk@Y|;u39-W_{PdCtE<;(=wm@31{p==0K3|L?W z-i}pQz>qOvLa4GG9aPOsE&{TkYWutdU;0)&??a_?LZet{Gx)MG zrp+xAKZoNwJ(tl!H3ZeA=l?Exd=^JSYyHoXhI5dM7S#_w)|;gT$JNfW&|xjgLGnq{ z=$$l4hYJ|1o9LAmWz%nPJQ!2~%y6?cYgs7c4iIRMOYWa*;v#MAKt8=)Gh#Dg{7?vd z{L6b%J&C$BbQDaLzaJI%q=uQ#O4BGKS7sCv&yQ2@g!T&Xo!{!m$jN~+wJh7h4OlHH zleSSKet{2n;~g(ylqkl58be&kMY#^tYQ6;;(u;D^X@Ra$y734DB=1Gd|5vuD7ZuE> z(FjK>z#wJ`G{bK(3_1K5kNGY`8ICZ^`7c2?9bsE+z63QmQhh`}K)f+g@i1LnSdW~X zD3>nceIVg}_`z3b98Ti-v-bfkn4wtZXC`Hu63|K~D!_C89y|%!JlTySlSUwgneovs z92rVEpsN75xa7j*rqstW!_ATrmYVNO*$*uNoMv(>g_Hy|QlwoDsqTAmssd6%4ps#z zOv@z7z=?<7EoJ}@Fip%cyPQDtovAl`UDX&D>IdJ@eKNeN#k*EzV$GLBV33yf%)%7~u7&L6hqNITtoHx*7~V9hVPf4R6#}A|qGIw&y>2820!&L!9y$W^^D`hE;Ra zbXZASfoZOkrKP6IU6{t}Zd-k+kW&ioHdLZ?SISNIAD*b%6{cZ;|8%9oy8Z+IQ&%B0 zAJ-LB4s~D#^W5N63&I_Cqn!RQ0{9Czm{}nv-6@~$+@($CDQt^He%Lw5D_YHfkwbzP zmjoYnfe9l)Gu}9XQAgfTXU(^tcoX?@Q6d>m2m5FC5{n=OAqlYC3;3n&Uxv z@jIdu9#j-@0*qhKY5&?dTw&d>e@e_xKLuZLcQ-lBha-dsq~Mf_zJ=}=DE zpYkJq9Yx#vQyxSOf$sOG95Fw(KV@#c5coim;NZ4uZ)d_|C;gC+(6}0GvDsSKE0M)lAE|eg~PF(zSn(2kh=x|*SgwHD)pCzXr8zhGU zW?TVd+Fw-UFT_QDFT44NQRx7xJO3~AV*s_+7?a72mBSrkU2c2k$-tk7mn?LAAZ2QN z5T?E`AG$U+1E->4Tc)QCVm%#5t>nj|XdlXf?}^s=fDcFKEbdg}uHfB(x&9V*kNKB? zr&r3nI0I{fe<276zAdGGRVDiBLyadngWQcG2T^hQuV4b25QU#W=>zw>gQ%XyJ2Ie@ zA9qgi6R2bGw$r=361DTCO!b!mIi?=Nk-Pd*-S~Y`lrLo|5WrnKvU*4wp)_Br(yAN` z+4JTCg1+hbc){>L-C_j?i75DWY!y=a!3qCGIy&b^P14-}JN_{mc&EWsPyP@TIG7qw zJUoVu4W?`jyJ!vse-;U)es!g)b}%)J4|eDj082s$3J#!5__ioMfSS>+6YNhTquNZz z0KE>NhVv7U(-5i$mUal0C$89yDZ(|KUc4Li7)tp;Z_ge|SrDHNqxD0nk$h8BIh2~% zE}Pr-wJ|%6h7F^FEI18D0s>U49B-o@_@{xN|3YVnQN8$QQ1vjXRHNzv=wcu>g1-c5 z4X0fBO5`ydyqwdHaJ{Cl!}Xe`zx+`8aLQ00ElCM^KTH>(Eblyufdoee9%+3o02wnew-zHzOzqOk@~D z&F}OLIzz1wzl>J-*@eQsi`zF!hA0Xg`g_+@qJu%OssU|L5Y@?KyXFL_SSI2nNbs`` z&fFWfqhCQ(4~t!JcGHN5?--|Y*9=!2p&3#p3Hgnrmg!o-m6=*sdDfy4-5N>Rv>yN^ z#zev)TpGtI^T#&SI+E&Rln+8gM!`G@J{XuCm&2ZxaYq58DC_P8V3)8r)zHf|!YYSI zGLsTID2Le1@Ub#1N@B`U>L@CLk5KI>%0{F9tw=0|#`i<^QmT9ZO3)0p4Q?d6wsGNp z9S!eJ;L1+JzSmZ~Rl)w|NC#NxxyG8JbyCXFZ9Xu7wsJavMln5sloJb24V@qZNXEDKd4oXcTg-7!mb+ZDU{iT(t^Qq z*PyCk%3PvR(`GDvyd;#7Au5EjS2v}phoZ{xpig9t8vNTTS;^s7E z&eRyEgqTUQV&}}C7UMK~>a+QPOGrQNq>2qV|J582NpE-5Hi0QLsi=7-D(aT6` z3fwY`at5Xl3|u5^T3h@c;-?TY0d}Ofgsp|R1wt^Sag1UA4m*3R){UW-^3ATr!WIg` z83-5JVw^6GRdpImJtF>}C3aNx9Zz-Ot42(qD*oTw?`Su7%7j_d<~vT|@yu3BShuxc zuMir+DWe1V?~oSI61F{!Hcz5*1ZA*=-efRab!{>wC5Xm{ND@QU2z8<)?7Z=?34d9n z-9aUerNRXAkv%l~7Z{}2>&5;3{|J+F75<+pK6^^S=AQgVxbt}u_CACs=Ok=1gj@*Q zAWX=Xu)3EtVa^5EWvbu>GX#r^@P6Yw*dW9WkWU2KFIiwJr!~RzEattbL%-3|>k@Vj zgwR|GdpQRd^2-v|`ig`dH|d|+A6=8MBA|H#p#(ykrv5~44#NB9L$J_8FoLkjK-wuC zV803QB=}pw;Bn>2r=jsPsUAYCYH2t;@5Eh#GGHfjAq&Ah2vg3vXEe`P*;=2K-?O z+a2OMi1jih?8C7VHWJdkAQ+8iv=&KC+qBq3FTO@Ke7ZBedSVuz)iZ=$8WkH+> z`ErOuAzlXY4T$$b*aNCu2w~|K3A+ZuRtV`3Ek??|el!W~d1iO|92|Elz z##RYyha_wv1mjuYv7aMh=R?>w8~PnW6{Od1hQ5WM2Rxi141ur!LZA5(Hh+PHodkC; znF%1IB_s|)P&z{4nG$yPJUAFZ*azX#Jk@|XQ~)3U8w~vRkVl|RbE(cc&!GhzNcAa0 zC>E$Dw%6L9toa^+wCUlkLPP!KfBr2H6m0u1UYSDp2TCKN6t;5&rw^Z*61_ax`0LkU zydk$de{IkG;&gbQUw-(JvbVns8$%wa_dh6GHfj2Vq}?5(-d7$ku_`K_yeL32cYgn+ zP3CT=v@IlNWBa5#+ZXgKe;8MBxgq#!tZueNpP?ylw|5y^=Wr!r{gGQf3$F0r#eF#7 zwLa$dIE0@^>qUpLr1cuO&wr2nedW$$Rez4}!%h!L$TE*A>V z`}V1JUAg`9{uM*duKk>u6FN7>M(=&?;i0jYiROso(Nj0(Y%Z9#&T|FvQFL>-?S$83 zC#nMdM^>o*PJbiX6ZkOjLhyW>iRX4_wUZ~G)V;gnXT$wh%hq2S)ujuu#bZs$F`=sD zn2sx7Kk;8$YS*Tv6FQ)!1xFqBUn2UXJ=S?xoNg_3N(?NTXs=u{YWxV^-jKKlYY%;& zy3+q%vqaYCLqXlaO%I$e?n6%(Fc8^!Pz_!z{W2oWu_>!hD8 z^GiA1Z@`uLS6hCio0uF(Y%Ux3U6>W*o7ua=u#2A$$MM|zq)&TnC@`E`aH(-hb5z2_ z%aR`hZ*v;YHT>F_Nzg0E&i3?`!bH`i$aw}7P-8}1h;O9-( z#gs+Mf(t!-;~ip7J!>~AF3n3de#hJn?UzLM{a~}rt^W=C8S<$9Pqgng6+UX`vejA= zUB~*S&DQlQ?Y%w8yxII~?wOHuM|54{cT3Tp=s$8z$=Qw&OjIRjO5%azt3`axjN0b=6T`qN5=2-Cp2vT%QGi4;dIa< zYTtSw_v@S4zGu^f4O`7&7c82`g!U+dWv={C9Im&vRQT**% z?Y#6L;~^VE!|w4vj2oq$HT-d@TkXQMsF((_>#SHi-A^HZ*~}e$r`v=|Ref^+{984f z!|iQHvh>T@(!})f^0(!Yy(Z+Dd*r~`bXLywIw#`vOttCg<_S!R$GrSWRKt+v4U?~R z2=TsKxz~H&iyuV#8db?fte&L*swW$($H1B@Z5pO~V|lc3RjpB4)y7TZhE14M6LPCL zzVftP=Fh_iOplH~pIETfe8{_-2C?1bt?J&BGlO$4oc1fU_skdUO#2ZNvq05puxP@{ zRFB8oZOChX?OxbxghOD( zo!xTL(r=&kH?LH>Up!P~yy>3Jf?mrVZSvJa+I3-PJn@cxBBrie<=W1nR-S4qT`~z3 zGYjpm^r~?z)QdUW?qL`A@TuJwCng-cT=w_g8NYNDC(An?X*cf6Y;xJUo&2bzBh>ei zwwtbNo~cWYN8lc*a_zGcxTN=h4-F1-6;M$n@##Lzyfk{nalp1R5ZRnHr&p82mo z3#^KW_Osp3`n{=~Uf5IJ`{|xj8PbDe4bJY!-~8)>{q-Gp_-{Ur-}gN;dF}J`k;Vb9 z78P5|dN0lB+1{+H_wol>y_`>${aF8?c=2_Y+23=jhrC@_HaMkIhu&T9U%u6K=Ei$1 zRj!@Cia#wmwl9Fq3UBP)bZ+>I&R1-k=ib>~Ew4Bud3r@1)vWuu_QFxuHSzkphe_{* z?aO#FEqlY=Wr7VCKR3*rpH})Zr1I*G^a^Isd8&I?$?mX>h&4%;3pQ4MNsm%}=ytJW z&}HSInWht;g&%lrt(rC0?o)5)BN7!_v!`^b!M0il=XtBX`8|9#XVx~|ZYEa_KAAG1 z`=-$6OJ}7KQ4XpS2CHY~f7N4X2I^TUN(-uZ?l^F$<*tQcGp1~jcC>t;^CZc(|Mr_z zYhO2*2bQugj+MFvte^g%v!43_wb6mcUCIaBd$A|yPF+9m{i6QcH@KbI{9oL%M zA$eiq&6)hUr#9K$K-4*(mzjN+2AdGs6{TMeT~5|5+%`JY#O%SWu*i|_Mk{9SY2PPN zZoSt!kbXW^u6QQ-%6A>Scy;lRR9lnW4L8yU?G5{}%x_M|l_f^r2@?lh?s3Ry=$azE zS|e?vabePL{dyhu>i)V`)<@K*BmHoP>+VsB8&*8I*+h0In|iM9!-`yKgr6; zT{y+IM{L%K6C>KcQ;aibI?uWF=~Q)>gF1`vZ;l~G4Of*In}K>1|5cAKR?nTOdw)%i z?2x4hd{Qyp;m3uPw26}xqaCuQbZyt^$^B8G!>iLT&e=X}<^Hz|ZAtToyT&DLB#%7J zFWT1sUgObm%&@x~vrZ4cha!)aJR^;=f{SOIOt7_ycsY*cTly_ty@mW0&}-E^pV4kj z6MOBhe?8XITfA}YDaUT%A1J4iKI^;7-`pF%eBG5Ed%w+enmtha`24ec?I-OwWSCEH z-8ZgBWN_@Fg*C=?Nmrsie@;G8Uz>Zk+l7SUH<$OkF$v%DHcy||TAJPO+)UT-flJc| zR@wISp1Npp<=0oq!#BOtsVa{-YWb00-|NLgHyxMy*xg3G7du+B55Glb_Pf_;_3g=q z%#Qnr6P;8ggRy#6{Z~D6v3eYacX+ZpRXoz|X5o^;Q+7*M8>gO-Xj{d8v56Zn}Uv~Oz+*i(f+0O#dX&V4?FW-NS-BHu8!%{e9Y-t{Fk{-o7hvE z&fRNYE7dc9(|%fr-f^eV`W|D$n%#Z!e)+yuEc;xudGWNTfsU%h7+!Kw_(A@Cxwmnz=jW_{bXvXQX+=iC#IkScb2CaGnQZ-5(D%kvE5TSh zbJ6sM_QwWqA7wdvQvT1u>pVhI_D3#Ee0x%-QD^dyXnS|775?v?Hu6F|Yr zqr(%Z(~3LDuhcj?-Kbylb9HOyJ-d=yGVd+fc_j1k_Gx}X&#rDJ+Y-JX9iQM$n-dqN zoRyrL+gwebnc=kKSiq?C4&B`bv^NglVl}go4NzO$oK@JTat8u#?vo=enhRh8Vs>RJ0=^)zAiydLnCdQ$2!X@pbau#tm`?fgGE z>u1mUdVa&;?+-?|hPx_a??ssgXWnhe|IwWC!_LLx;@KhN9OHUi=xz6~!_yHN4jb-n zo4h+dMRr)#Byk!Pdbnqlvsd@l0d5&Kp4qm=YPX!jb_#Rh=*u$(ct4b!@8^B2OD=Qe#qOoq#a9QPQFjxjMtpNq{1AMYYCL+#i_fj| zGUeS{zI-a0w`efc#d7ndfm40g1%5v=khE>M(bPF&We-%Kq%fLcyVvs~t(t_1P|5rVB z=AfQ&D}N?zYupu7xZvl))%zO{?EX6P>A~x!MMFjoFu#6w=(BZa*IK7rY@B|SeH0oz zG1+KzdGdwGH+d+%HoWipjqYx2+Mx$SKD$rPB7 zb&15I8)=`L>Lt2kHr%YgVenYrsl0xk^6ICHF52o4d2voo<89H$2Nw@rJ*2XJ(#P?` zh1Gi96huid~a$MrW}~RE6h+J zIq~#d-PzJ3S1db<{Iy1EJ?Pik;ZmPm&u0lmRs(vutd=}JDOs}Z<#?yDgMFv2au}5D zaLi?Lho6i2dK;G$UtLutJaY8wo`24_;pVw~dH>AHF%@8MCQ7%pQ{QOyoaZ_hTpd{^MH=;2!-r#{2zI4=j>$$JS8YP5*$Yjf3ojy9<9Z~<2lH8>?juJQ3RSg0~y;A+{xH{% z*m7R~{D@24%9sCj%xv+@fM}aJtD3zAP1;Z&uq*j(kHI5urZ|RK_M6!4#Enj;d$}Df zyBAi}`F&GPYrwjM``sQymhGq=r+QN~`9sFN6Ib?~H%nb4+;HT5Pq92-kDXDt@6`7c z@l%Pj<)wM0kq@_=tA4c1NWOjR*g&h%dJg07%sjNry>9)u7AkC$@u=|??mfM_zP>$A z(1c71Wr3Abh@VeshvQy91H99^E$|%g!yb&?A(dX?kBds1Bzr6^>-_z;`^PsgH|q|y zx+*yTxHa=j%E$cm7W?Y+N=^}%t=24WvQ#Z^>QvrafBpZ~(%fF1K}%!a^m`WF8kl){ z@0hfrCk`&m0qJX_iGxP$eazeSJx?_51K4zq7uD zIA*Er$lXCvD--7KE?stL(yqlZIrHCSmFzt${8|68G<4y*2%~Lop5zSWu2*ISWqzIu z793pr-Qh#piJ9kQ^Ddk_FZF#R|9qpf$KjJ?g+~S1!J!HpnVJcGk5hPy}zo* z7WrLx;O@|4Vt3@1Z-xGU&aG{{PrKL3mhiUa)#Zk>ztBG`uztabkGZ-xF7DLUY|2g5 z-*UQnns@5jw(Apj6pK&!{oYrIt==Q=R`yq?^S|q+&Ur1ZRl8{~%ZJR|2 zg|mvTYxp+l#}sqXg$98$wf3z0-XsxmrshQ6%QUs9U$ru87woh!{JP|(-;6ts^WL3$ z9L5kBpTA{hh$|EDSZjF~$E((V_*CDY_nG%xap$uWJ!`{%yqWUIURC0)_2XdI^hpQb zF#XkSKl9H9h;4z`Zu^;k_5q9{cV_%&De&RewkuoM&sKJ#gEMEBz(hd_k>m3>-Rr%S m7d-V6Q%l^Qb zn@7IiIQ&aBK3j1s6woWEy+_v`Ergi6@Y+3ET zY1GL^-Uri7hXdbUKln=N-A^`X`32H>dtJh!l~cwXnEBGQh|@As=y=<4`?vv0dE`S+ z{quPP`$#?e$}=pMO?2}3xry`RgIDyXN4+mU%zsYrtZ!buZ}?O9^W2%GgO50>K8|)# zvV+ zrmEMcKFgb21wcZtFAq{qtXKWyhzESHnLm{ey=aJjzBSdhZ-J%50p$ z&#U`&`mT7M`*;@HVO;S~+TLxo<-u-&Gc#`7oU+u-?B(ovi9upd#l-Ca*l@0#Xq*i_)QtNNeh9brj>2A1`DbZ_MPDD$5Crr$p@Bk6RnWzyXlw*7Ut zT;9YUw)BmiAVqnu_nkM73KvFXryQ6yb>q!9QR>QLXKxle^}jeT>ylm2o)s6U=)P5K z!-rmzhJ3#s$vVs$WTU*+I^^cz?miD(K1x_|WhUjD_N-ew`$p)l%U!6rbAHpc@9r`B zA>0QTHe`2G%rIwfCy^kIv{yLWvhP0A8~cZp|4>N| zsHFa|<8EE^D!voEtjSmAK6djhXxN;?wF#Hq(sqB6&E^z*t>_z%+IsX#cv>|j-bJ$8 zeb4s%8`2Z0dnt2gkD?KaXKyO}`eEr8)$%Fq2YETF0a}@U8&4VU8oWPgm!i=jQP8jE zURtTM^+o@bu2c`CoiW(z!SQ^XIdK*L6r8_2KpN%5@%+?ybh&V4l8Iznc#=iAt>Khm zrdWO?kBG&+x$vDG+kSk zp8qZ?_(8?{`Y>mow&mk$hTHo4b$Q8IcI@z?5r&719n;RGzdWAcv&i^(qx!q`;EX-x zo7Z<=Xco%JC|p&=;ec9dd2Y~)`9n_Z-`bd^1%hgOKkeg?Va=;P?kQT^98+|X?KAE~ ze7QI*rsVpv%d?(aZy)^QkjuI0pC0xqxH3Ov`V~&S&c%VbFR5gync{TsxV!3QKh{w9 z<3`t;jWM5kUMrP-*E`(1zuX-SbvO|EW%A>;g07M2Z4o;Mp9BI|9ru4g?p{?g(7em2It zWJ8vV>E+?GA0@J#E^u}(U3SUAC*m9n1!dXMRDO|Wqa!bbwH=h-gD-i zzcI38Q$<$0S?~Mzs{)^7yLES$G+iuy*nbD?3Iq2`X#h=Gq*i;v@ zYjXK))`V$9Y2+&BI%s`osr9y9%K0bXU0Yl|z<2t!yOSMNHFfOG=Z;55PK>d=!Bb{iz`DSy zZU3nrp2}j`&m1-gOAEt>L(qf3%T^ z7UAbCs1d4ZhtVt+)!m+Fs6OupC~4&^E{i3}fFcPlm-N&Dq-9+dd$Wk!oW5>=ryeAb z{Viap7*Y-L$;RPlF--QiIV(l$hq_)l$Q<(`q6b>BSU;lG_1~c!9YKVHZa=`24>hLr z*3_V9i0H|I0^J^tTY_xtxAHRJDVsl^!03NeJn0JQj=oFp9+r7pcAYEa=U`66lR* zC{vu?nrtL{Bal4AFk1~w)h^16eW6aNR;S#}OnEHPD;26Um)6s4D5k9J>6JPVd)m{J zaO`JKZ-f}PG#JNk1O|!{QBGnCZsp=fyI|Y zQN%7H+&=oIP)%Kq%fAe9>33o2SMv;$a!Bu1!bfFlpx^~?(e+)#0BI%0Xb_4-24{G< zu(aM>yV~FkgUuu=7zDm{wFqiqdIk!U5(ZHSFuK7FSflyCdR%@0*H9gohrb4_+N)&| z)C+kCKQ=!Zu=DqT3SdZH)?Hh*+$0Bo^>Xf_j9fxrHyElEaFXq5p%Kc>;Z9%ul1w#69n_4XvBSk*p$WBJ}`Gg6PW6Wa3j72~43E$4Rhbd07 zE8KjX%EKR7{ZGiC(R&CBgFk>jFmT}>!p9Z^3%Ca9+H4jpfhA!HVLGu`_3iMnRny=4 zqnbU0nY6w?(}#Z%a?RBeRsbL zQlWIg|E|V{g7w}|GU0a#(~>Q2DHjSteitw;6=GV|P~hAj?c7V4b?(&BVu}$c?@t0g zXJMv+c+kzggsqh&WI(eRaTRb=!svq>0DR>p5216B&OXAv%N)(Lk*RsfTBB3+U}O_G z^evbQcrKNpk^2ZsPKh&$-$(fLZPEhk5S=hcOnZQ3l9db8DOTzfdoeAA0>Gz8m8DD- zAQDB2>3C>a9%8Q~ol)&R!d7|@YNzThYv-isU@h!?8BfRbR!wdg&hasP8Kh`=Fy=u3oXMqg zjOoV%!TdyuBzd@O4wOMb`s3vObPE)um!!ClNHN288*sTuQ%I1mF@!Txg{!RJB9r#Nl%5p)hYps7ST(4iWn%`m&$sHre@>P%W$Ep6$e9 zvD`~O?wv3ZCWhQ37{JVAr-Z<;++--u3z<0+mFmx}7phs2;$(N#uyoHIm_)Kr_SIm4 zZl<3h<(Dpi;($TyR@pb(lxJvZ0b#|yjJ84~4F%M)h}=;Ob)c3z`d2DX^lvJfNy#Jl zvhVC<)4cJJm3`+V3-ZQ5D*bk{mh1{l=7_kwm_HHCE(VH)5u58*Q=6dRoq6BwSVL8$9NqN_AQ07a$s z+)55xzMf?Zb~Hqo)_@Orl(5LbRET@4vAlpJPsJD=0C|J}KonDnK@tOh@=#%E1DCq~ z<&Jzg#_bj=u&dR+LNS?f3>p|5Ha0zMmO6#cq07;;gM=yj2x>h@_(I61@ z7j{+A?pI@qxIm{EtQL#9Nj)fi$Gh_?-S1?wo)RD|mNK9XlNux|5z4*=q|{|j`!tTx zBWz&#^!hhySRdZAY}KH(sW;galNQlS01jg5kO`l@0gdIku~5+=;*_+skxL&?LB&nB zTDEGp+u9!}CI$0#+=STXjZlP@S_I6}X^;VY4Jrsu-LC!@IeJ`d^Rz#a$NUfEYnbtW zkz)Y4AA>9f&IOVJ=p2~ggbn@j0gP4 zQ-oBGCM}?HmH;sQHdUAbu`+c9eC=)7=E;K0Ky_k?6_t?+kic;iLuvvHP9CODp{Yj* zS0U4I&8v=vN7kd=M~G0Tg?MUr8^Gw4svYsQ#b%XaDA<8cw-?Rsha9kk6d<6$ZX6 zLdhEg+_To>bzRBB{H8)$$s3QM8*pk8hC#~UOekN;n~LFC7%su7Tw8VKAfXsfBx?*{ zm&rTH8otV|v8hgS>z*ATF}DLI{*5N)|AO)5CX#w|@fhLI9k(xMb;rU51I*G&>D3_< z7#8^)PY}0=_D4E}L_c)sIAJJF1L>rfBu|v<&48M@bKs*IWN>Bx^?k+dWWDsO$)xNX zJEfP3Lw!A)w-Utar;)vsoA!zo0NW+4VJRxPfB|N(-AmlBwud$jPF>o_N;yk0G*)&cT3H!cy|M0k+!Ms6aBg9z8ihoD(DiE{4cW0J7&K;mb*`S3EySn6PzM z;G*R2O5Tck#6C^*30(*P>WbW&I~OMMl>o|Q2>|k0kjqPjl>2Qh9)rRlR74#W0=xD)hEM7{6;eyR?}Z_?mJxTu zMYQ@kN;^Y%=;IoOcc?2)?{Vi)=^4Vz55u%VYbk3?vr}xH+za0NcI_!iQ7%5Y1N*Jva^ZXs3ecL*mZopL&RD6@;S{H-iTvwW(Ce z!%g7~Ly%(RspQ(P2Phb$8(kn`CwOBiYD7cdv&5Y@my8(?MQ%!7KM-<0pwZ{NVX(RO z@MY2a--C4(!ln>RF6_VaaZ;|wbjF&Cq(wv*=|Wi2-UFC=0efYf0Sg-|7jfk*yxc^J zv8fqImwZ&KfhgD?7F9`~(B!c4Q)ToX$Z3CsQeVhxm(f|8QXfrjr6z~jfwF~~9PAfy zF5#mZS)C<9Oz?4&3hYP43aAJn70L&beDMvMf0pR3w**)+Sa%RA!1f;%oFyFnB3l^a zNY}OFd6hDN^1|D91KV2QRYYfLZ1yd>6;ue=8`}(6k5hzDC{dIlK{1%er|%dF>8&b~ zJ_Be#p}KQKC{MjxQ*(L{y9%r1!M97$z0ff_Q0ghLRHC0U_^2qr_3tg(-l5+ z6Fwf6+p5~#OEhOJyvPd^)M~GidK7kn=;m31`@c}bsCx;>4ey@ZZ>!AY8PT<1n@XHh zpYxOXl*tu9ROtlDibS=j-~wSYWj%CW?uEgiLfWu0XNq9AfT5X@kiZ3a|HZvmg_~#t z(Ir+o%R}*on#vdtwB@13RK`dq0c-FKNKhG*;G4OAC5MA#ei2D;>`CALmqod8Zhlj?{|*;+)DQuZRJ5mfA8voKug3dCMbeRpTmyY}wQ}XRRo%GRW~}Uo@$bFgN&Br5-#U zlAC=Mt15{)HfLYGV%imA7@Jdm3l&}^`Z&J<5hw$!yn5rv+}#UD#u4=7AO&{lda0}*N<12JR? z12NV>u+^dy7>KzBf-M*Q;~`_y7z!<#G*h=xlV)l?c!XW#QgnqOE zWTRu$CvgK{9a)eaJ3)OCQ%@M+-b%OBq5ZdrxZbPkGg$D8CZB9MJaEO4Fy9rWqE}U-tUJUJj{99j&|Sie z9e1uAM(PJuS)-D@0{NE}siuJcYr+%icKWF9_n z_Csy3Vh`9@SWKb1?inWiW`kuj7TC@#mBoPwb=Q268Kj5#4v!VvoKOnkT zjs!Ysm+_Z^JxD~oyISQS53Pq<1nOK%xR}^NZFe;lTBqtJ2Ljw;rf6I(tTBLcuoe~$ zi0{-Ao+g-zoT&h`fKXSt9u!$UB$oHa?8--3VOI5UhZt5V6U=oSWsQ3QYZ=2g7xRs7 zJS4n)xWGRv#b2(MT!wdws@{p0jnj&*fI9;ArmO`v%2b@j{xD@~bw?^rnOb`j`92~J z_@sg`OJ57}1wb9ncW@9_@^BL@Tj(sq<{w8+X7KvSmkK2IP00E&VZ}+ULB5ZPAz^q- z+LOD1s$~YT)n0gbry{E9 z*Kk0}1G0A>!vKurA>%p_9?e7kb%c39$pt)Z9DzUBd8U1TAYifPcY;RUTSasMl)(r# z_XP6*AUPmh9?AvRORI=ts?aMhpv`rlwBWF=uOr4x<&B1k9RM_72NTei8Qr!$I<@JB zGUzK`49A=*4i(gvnU+jLV`%}T%*g_>5##o z=*ZVM!G7f_xbT5&)KlVb9N&Hl{?$4j(t1X?V=?u8M)b#GOL|5)>0+5`MbuO4p1$JvVs{qoZ7I-m>{%ugSQK|N zOK7N;tmeY((MOr}#6%rDgFx`Ov#s@nuhS#YGI?<=dqT5%fINyZw$8l+kATI5d8tkG!3szn_>8D1h zkqtyQy*JQ1rZlY-I=r%hn2pu(O9SE6oiQ%~<_3z|_>Wl1;MVfZ#?qK)nfTa+{YY-S z8LEUe67gaV&^1tei1RVQEi4cX$Mcaf8@MDhQeFDol=@iOAke;+DmcxCX~8LN6i_B@ zz#e@KlsgZjm)4sW8-YdA#zTnong|=t8y<3PA}r0{L$iTYHs+R|0r`o!B<2aQj%sA_gqb)Qs-GJ#%C-6Uw zg(FGlZBoa&TH5ok>))l4#sA+2epG~FklXW#p-)sS>9Z3!(q3hx>n7?upToYWP z_L`Q4bA5(#mzaq(1Umu2a54S#7$aDf_Lnknq_C3NKQ#MxH0d=#=pMtRSYC=_Xzpt` z#XyfTUK4|)?vp_7%!Qvli~!9Cw^*VH7jD;_(}wafp?pthn_7;y*F|b~JiBaRuc2H% z6#%%Nl82dt!GaVc4<;4R0m~1D#IGf_1k7m!(*@8M*x-wVvD5!GGqLgjp=Pk%A7dB% zsiSIj+DkA3;I*)alP>j-U5(E_DfmDJIj1{JZ#DrE$dO(JvE0P@4>YGcTGnomXV7LG zMaNWxrEmnGQBO7WMM{!4bDi*B#M_)((hie-zPG zkdLNft20BYW5Dsttbdc<5F#n&%%?0_9|jDk?xajFWUDytB?f0oD8C9^s_N^*u$kg| z0+-y*h$x|ZWAbkVt0MsKwurL2$8^{49>%9lHErvrltXJU*~6gSFe>G)D1oopFF^NI zIzBvH2lakSSmuw=Q^yqTUkj(!&7Dn*=Cwy{YhprCBKV5`bGJkaEk% zF}v31+LAYLui*6_W+mfsp`Bp3z%Q7W3zbXjISFuBl|bbL>?^eq!3IF>l?6p~4YR7+ z(r(}n&+`NfID_&vYOJnjVnn_p3^|`_ki|P91jm!#5vy_h_8rj|$K6{9e;iM3A$sY5 z0uBr=5){{7Z33_zEnvVPzSu&n<+Rl(M!hH0Y-s?rL+gKL2BzZ@5c@-@7tpv?th(O4 zgfSf*_JiKCpOLZLOXHcsU#zksdIc2Zje#u45vul(oA5y#ay=1%e1`LrOynlfkPM`l z$mg;_CW3kts`@|}+6{%06jLP+$C#XwH?kw8(wA%HkNk|#RFbp{}IRsTcXBy2jKJmjA}m@rjOtqnlW7=EJp(r=EcU)j1u4R`M!b3S`Itgv zrs2Xs*>|^;yT6VE_?G1pc38ZDmf&>QrxMcjkOmpUO51VkU@Qs2@GHM?P|O#?QhN9c zEV35vw+$4llX0ueS9V#D_B{X$UkaU%5f}>0W>Wx$DFhBkbByKvy!V5|~%+6H7FmU^DbU zi4h!-YwlNKnHN(lh|Ay?a5e89(y&k(J~6^lbuM5hTNH3o4B=ITu6!`xXL<5xB>qM? zbJ8nO+Bd?#2j;2uvM_m~ruPgff82g@XlesTLvnW|YWhYvnPOZG=fLy^oRj6x`HZ@K zhl$TsqGR6)S9?Z;FWgI_&aMYlrXIj1;o~96d2CZE!CwfYnKTu#f56q5trAS9z;6L_ zqLes7j&A&*Ru6_j4hxHv1YiZY4S>8cvCRH2y=0+Xc!E%pQ3qyY0kYZ=aV>%bb183Zgru?)b4?X&^lWrj3~#c+{W@BUas4}fI| zy1pIPQvZXQ5g@xZqLZNlL+Swneu$JNNxc$g`E~KPF1Ft{B2k4 zeg)QRm~m?QFF0UhAq4`2{tK!%Uk2@79a5G4*dQIY-z-12LAn*hVlljWk;Gs$U#49l zIH1}1C5%@gOa%H%6L>@zJ)D@20V2s7jB9{7$}1cQkQjODT_6ODg@_8-F3H2i|A3Op zL$(8FL;cmGIV^)!JHg_~W2@=Su(YixNj3q)CsABMqMwBC_*#vapbIwGN6~;OECLEp zy|@)JMPH#y&F(;f_JZnE$SJxT#IV4Y*7IFhtoGSdNTG^HXTn+p0xd`o!YYMFhF+$N zUi~Co{2Me651<7TIsmX_Uv&+#m=(HG_D)yjDqC8|N-+kW+*>6kSE2d4Ell$&<6_6F zD6F06$9bfK7@=*tp@3-pjS{Q56gvD3op_x0FrP4Es?};7LlfWJ!eMDSJ7@qvZuo-7?5Ov56}4I?elNCvsY<$$j0;VQ5$c5DTFYLczwdbm2kOd*F~jKSl! z95ls=v@^xX)N1?{(L$&WtkK*YwAG31!l4l=b|S4}7#<85Gw)-~Fye=Ig>s|#63js6 zaCPEUfqVtO1gO9PBDo7}MD)Yp#o9b8`i#Z}O~HLLJauZ;ho@|s?GE^Deh86UPttW5 zB-AQGF6L7~{Pf!?WWGHHXiRQN?Q&(>y+t=*lf<1@twU4pUOn^2!Mf5RO1pK`2ip>t*ijbDF#ck*`mb8BHBJ1E$cV(urb9Y zU>(&d^>n8#XoWM`tJnWsV@yX4t^Zl!LJoT5O!miDc&09-H|I(=8tpzu!SP1 zNG`qqEt0#C_R_bT!2Xm`@99~ZnF9(75a_CV$-izY@Vvq18Qq)FbW7iKSLNXIyH+;0zV0_=9AdPmnhD zIN!0;g&AoNlSxe9215}8L)tCzu~k}`^N-D+$$p%z&8Rn(OmC*@Gi!L*Lot36A3rbd*DOvGBx|OLyuMtrt9CGM zm%CZmh9+}K4;LXYj{kuME`?)DO1%U_=*&QDYU-I$kS(oOCr<(w-49g4CA)Egf1np! zaww<20`=yR)>7Pe*-{}Zg$vTsp*Z001_(?fKBYs!D0b`}`Lww$SQM6^K6Fl@6 z;6(@EdpQ(fjJq9x@kSk0$~piWdvpL!005SEEmluh!=`n`wn|9z&G0U|xsXwa?a|*E z4PnfrmE6m-4FwRSC+Af;nop2j&8xx0%LDOqGg%4W;8-A+nFUASGLRt6IH$|eRf0@% z@`S!nohWM@i*~36x|hhVs^v@hAC7*h872yY%N{<|qkFDJQ%SNn$L2KJOp+r_+#8uL zs;t7p6X5+Xz6I;jh+0W|LjN|)?|V=DoDFK0lccBx0Rw>0&6n@FNhj&T0c8Qvbq zb)y+0eW!jX*xr8hWea9>n*V_v6mThd51L7TzKNdclY=+|vrtz9@{nacjD|!4x-I#L zQ-Rmu!`z@!wTLzVA!Kz%MnclN=ha7yWbq*r6BA>^JHU~q-tU{&_6KO5kQ{BA-hmH~ z?xb3F@5q7suhww1#_TdvN8_5@tJWQ=^K)Eg7p|u$&ve=UUMD!8j>RCelYNG z^LBhOj?5#|a3oy10!PN0$UgCJob&A0h7^Wmrez9d=^3m@8kSc7+ZChxXrd9BV!0JV z@HR$+aOhuz<@ZpF5!sb9!4!4s1btp@iu^l~(>UEs(VkA^c+P4Q#4#ovEh})1H~$j& z-D&5$D8!g_;_SJP<`|QvoMvN`VGKI2ZGtWugB6-`AH6gtr&-ov0ySW9G#q^Rmjm59 zD9(fovDl(yqzpEoHHaIKY=mt?T>^_vQ=$qJvZwZVkdv-)`pE0(s|jht8GjvhHYK~d zjs$SpHSBF_z6|W)0kQ&+MEBpOe)Gz|jp9v7dzW;0HV2be9#QgZOg(Xx zr4})Vm=uOn8Rm4hrZylQ2Be`WHoxmTYZ}ePXhVOYWrozsrF=LXARkMzpEDzvka~Et zlaGY~rOJ?6;F6X9e+vuku!P0Qs0`Iwk`BJ-?ty6=%%{H6tAA~enV!4dVz_Pw^T_DR z1>Ag4RYzACnW6qxq@@v-B^9v;x&%P*0_T;W=~iSn8-|MPCQ|{n5Z3>LdhShhz>4f5 z`iK<_stx8da*0%nxF9OHlFQ7&mm?})rXgqnM$2C1_(#r7W${POQzc+>$jln{Ee|oT zy{|J(UtEa?>#k<8%E!z~nA7T~ZA>w!i?3KLVT2~)tLNZ1C4fWU06z+Ff{&UhxFNWk z25`&8x9yT~6qNhJ*%`{+AOB)P9HbWEpBl)1xCN)E01Eyf%U!?B^~0?UdOj8kew)&& zv5=iWA0@9r!V-I&`aJ>XT|@2GWO3Z4m2mP?lu(0><$Yl3!6QoV{{=H=V2m;*{(@Qk zhDm?HY#A8do6teOVD`UZQ7aW)Y``!XwQ8=XI%ZG;kDjg=Ye(v^dv=D$e$X}LVEn9r z1%vq$&owY0S-`al+j;}VU;?%VsVK86X+|CZFkM-J4s|6v+g!w%eQ*o#VcUx zK{!{o!f7)gxeIWh3}TowPy_)ZXQim38?2z8;J7k&BE~+Eg+6y9Z49!(+C^suImh7R zD#b&*bfysLyS!( zrh5z(xT8nITw zkw(_8PR0|+T^&gqX@*0Ef0QsJl6JF0H+zsh1jq3n56nF`J*O_)f$ZFU%^v_bF@fLt z4~HY1G1YE#i7gu7Kz8Xe?EeE5Un9kqAqPjYJE!yr8s$jZc|1Or& zN+X1n8>2MFkUERW^2{OS$iS>1g>mWE3&25#Uq|Kx4On0?#Uf)CdgejCWJf3}`;cGQ z#`#m#>a%zumoIJlxK|!=_pI(NRO3bVGZ_aZaLNOHh|RYJdhqnymW8_XB`t=pc?fEA z+aMzX`0+Ja4>^O(VUWi9=yy*5d6vP7ei-0)PpHZDgq*uNGqxUdGY{7ntBorJ57F|z zq{Fn|d05Etb%sIuea!+TkEmy`sfa2~g?|xF0|K0{snEOWR}C`uL(W%E!^t*W)$c|J z8<`l7dqemT>3fr2I=`Djq26%n0C=1?8PV-G{NEPJK`OwlJoGL!jR`#Uh7$nr+^HYg z`(Hu;AK4FP0mSqAkpZKa`<;(46=KFJgRpm!rCiCst+5}HRLH95nbYIWGK%}#kqc6m zi)?*OmUv8=caj^*qW?0OW@p{TzP*+Y*h^@KWL z+iK8EA2O&D14;x?=5kyCS3_k!B*k$(heq{>{jSbAl+>S$XYWFF{YgLeU#PP$8N(rq zQHn3Ql%0V-`obBb=ph>6NA@KUP$ev^qgi9ftMtOd#S*~GA3zSrm@NZH2Llg`39A;vop%B1 zK9F42mGSt6l*{1^6}pECxgsAS$PCG$LJQ=>`BC7T1R@m!Nl#k_FHjH}8LqiH99V#x z5sSk$!^7rZONB)Dz9_$xr>L7Aw2u4dWm5*?abHD0Snj!!n6%_%V#wc(NgoVMicz=9$ z1FMaHCm5G^xDH-B2T(8K@^LB_R!&_Wa5W^5T+Tj@t_70L>=o!sAe@KCoWLV&iUtIc zMut64U>|ll&;g%-T(7MbO%5U_5R8t#LKQ)zM9(E3A4lhe!DMH>O0aAgdn|*V7fg0%FGtE?(waRQy$B{h+J1uTb)U+49IDdi z{=5uTDjGV1oTRS>?tz%G9mHDE znGs~D6(hNP%_dVm!Cp1+-wyh3LpCEx7j_#89Z6PeL`

    q2x$*Ihqqndb01My`kXE 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 W5jYS^VesnAj>=he9 z^eV**sGvwu1XNVSgRx-eqLSb233~5)-|zYU`JU&;^X%E(nc3Od+1WO`;c#*i=P-mM zO~Kt8W3N@8W_2l8EEf8}VfVaj#y-uK`0iU}zeV@_ygL*2oZsQO_s>J$>&D#gVIN`{ z>~h|?*=X!f(_!JgDhiKG9-}@e;4rOUvoFWc@T%XI&j%lW()FnOKE6-g;u&?0YfYVU z!qbWtSPZ4LZ7M$-f71dvl&<@(F?-hCV{)sA_$H}u>Y*PE(f*qP zR!SbnUwv7wdG+qHskHx?KG*ghOVqn^zvRfslu?%DLnDA&)R)XS1XW7MTqg?*4T4{+5I)| z8q{*th;HRi@V$O6wSp|p97jn}+Y#(%9+mfx> z^B9BXRCCmG)eB-h6LDK92wdc3Gw46}DUwVW=_!6J58YO^h5bM)DnB>J4g)0dfik2+zl@Os##l!7 zUApspZQ1*)dG{LU=59VeVEd6gj(?-qEwoI`D!OL>;$qwS{y!!g&bmA(BY${Yt<|WJ z)S1^BeyV55URB%~H8*Cr zaJb((!@bi3Y&UJ(*4K5ZgMZmXY3P=*=TgspsGV82wzIg|Uv1Qs9X~kwiZcc}LB6(g zwHF7^N~|z^!45K-qV*tT@K(}ze?(5tLY?}e%IR*~E~MwS)s6}IlDKU9UgtTwgSzG? zyv_-KwTol2YC{Ew0|u$$v{haAx02914qH!VwnGI}q<$i=-DcDrq?4v{!m{byd!tz! zrrec!{xlySeLLlWd3Cf6VK+Q+Q@$+=U0%{LuUc=N`}*11i!?TQ zbsKbZ--5G$O#fQXO?WqBUUqg4c`C4Bhts;tX)_(S9ki&~U$A_1S>N^*K{5LLCp+AF ztU8jWFz~nMN<<||Go;^rns=5(%@u#NQR&>a@+t3f#|m-JL+Y`+PWGPlgm6_XDc(ff=Jj@?nI_TTm(-RJQ-y5LWck5N%=IG@!Z@Bz0 zK4CqkEi?I}`mN5BJKN&K3zA}NXXrn?wE4t}k0Yy3Tro0i(GKH8kJwPLO9cW~RS@&Q?vwEE5&%Zj1uIZNs-AJ9| z_Oh3U?L79C*L|-PKlp?`7w$MY_uh7oZMusUeOzQEuZ;aZu|EuKw`z@c`(z7C0IR<1 zr($Fhi)A@CXc*QOhK+=v0)ZLE=d}gwT{{RK$J!dH?;!eV(j09?%1vQP%MbdKCGy%q zc-p?H5i01QP!@~o+d^t9uKfU#eU@0|yP~;lm_C+8L{BwkvA#zunmYlH&LE<{MhDPI19d3P zIko61A_i~(p|P7V=aqW1SmW{`=d_zh&~+HWVx`>VNi{Kjr!-WD(uD#ScM~=o&nKvP zH({bpHwhI=d^cLAwTHOI<#dp!Vn0zYxf_gG2 zI`wN}kCZq3_~9i0^Hsv+U83}umkM!}LR_0JZj4_6xb)S{v<09kV@rCYI>b(v^fVlM zTGBfp#x(`v_zhnmD-=aV4hoOYz&ziN#z?dH3y_Jwjz4AiQ-g6e3{FUIL=OxU-Er zu(6f1<-+9760iobX*L6C?#k)lpwWkjUStba!nt5H=Mdq|o`6mrB79?hl@fR{g#`a7 zq^*#j#C_HGCKTf;yH~zj8uJ6>&E__nDX5n@KL~9_Oi4D5B65_l7%Y3r8=ZI_iALrS z_8f>;<`8yT%^*6tR7f}eP$)t}kt~O>V(&oBIfO1zpvPiGk4N8g2oD3?La}v9IX4HV zvhgRS`6p!1n8Spj;2*&67`W^(;ckY3$GL)3H8zWtz=~%HVK}i^&0X*@Q_x0U=>B1% zmxSZR7|w5kEPX{h%LfSBJbxq1n38-zhyYRbH$us>3Ro@xgiMK`C-nSBg-I+ZmIT;6 z|1QP`M20J1lK#dpHJRa>vH&sjZv<0QF6LDQh|!+tz!9RCfpd3_NC%A9goMvi7-^s$ zboU5hX6y+~h07#tN}zIGqfW9ne#x+$crvBN9c1 z(_5fsX@F%J8HVbQ5@r%PCZT;Ou;;c^z$tAzrDNz)BBt%qKaks%$jET1m`8{ELa8n` zRU+0Dm{o;_isDCu*~$B+baBM$*b3LKB7oO~b#FUH^r-Qc-H0jwty zDV+A#13Z2wMtUoe@-Apxc$n103dU`)oK)!ScVOQ~-;NO$)A`jxMM^363K$5>vGCK8 zNt0nNm$(wX$1clth;-F5d&m1yr>K=j8mq3I0!Bc5hfSo zYN$I!&$$*Md@1MK;2QIKwQ`CxfuVw_h>a&DHcJ8KUJAP^7*>qj3^0KKpF<5N33o{- zOe2~K>1t>vT?3R89B)m!$}%%kT!;CMMU_(@i)huF=czgy67>>TR7iU$Z4G46(vbG? zWuQwoYgkH&Fy${FaZP&c=LwAI;W?Q{C%jR>lG4sHQ-Gl--DOiuT0}cN2(_b>j{%$+U5Zc+kUt-hDXQzCn0E9(WPKWqbJ0) z`E`?uFzKLwk>19_{wCE|lHxIVTmmhHnc59zb~=n^q$^Vuz7Q(vVLiN6BGu(XI%0OQ zG4(PsJbfdkg9@Z#A+=kX=2N>p0hn4%{U+)QrXT^Le-r{8|D6Z?P{n`dpZ?BE zl#+7)SpX#GKt4UT1G>H>qD)l>q-SN|4M3SHmNyB~Wva#)x)rC)Fbqf2Jnu-k&8B)vO$Ky>!nxhB@W08zdB0#hm^fdc1LuhT zeR2I#R$r`KSl(GGVw-NAK(|O2IDxuFbZ_+f9N{Lh669eE`A>B+UD6`;?Cz4{4p4kV zi*$q&He&_DSfN%$jQ~!~zp2hLRL#;6ZvUckOAl!JO|=(O1vEhi#!(|2k|kX}ZY)+V zFL9K_G*_71WoFD`CHmy@le>~Y{6v>r{p7B~9x%mO<#GYG0APJ2>bXR67YjCD@*cS= zklF=Mel7=6&mhIgWlQ-=+t5yivBXNEw7dI#<8LeZWg8M~h5skx_}^^f_cRj~8J_aS z7^^xhLP*tvzBfj^p!K^H62+CbpwiSr!MY?6RjO$VU6Ze8>y@cuE_hlzggIT=1U1Ml zyOpw$?O5n|t1INi@TAo*SQ7DGim8*Kbi9>v)0is%H92CTg};T~81Wh8rel99e#x8` z@j1~mAIl@R`la$9ZqiG3A^{r;p4=!fr9&Zp*^L^RrnXUQ__}Iwt>Z1ZDkD_NO;sVT z4rS#M1{|js=xQ!;mG1;HgX}D+E=kNh#LT? za;XV-ASv?_^37N;O?19Kr(u6do9!;1I7rNL$I0zUs)#hHami z#$d^^8DS!79jmE`CMp0TL=4ixCA4d(a_e^7 zICSt5(MtovR6^=0695!lB6N*^2?Q!&8ZQlKp}eq8pl#^mC894E%ZCd6NL}P`ndrmG zXhLHz6a6@aPtdB%#9$IPI%h`{%DYThNicid0ZA=IWvWdYeobX#CCqmCyfmuDsDxw#) zGT3|cR~VTfo-3@Nsf}VGAmjU&qSrE56|X6`9I0RBB%KFjz~vX-BA;+Frhy-;9k2Bf z1$cZ@FJWd$gH4zTO`^s5gp~oNNDJZBG)vP8Il!Pi@he`$0evZ-_#W4|I%{1j+xnQA}E|^Kn?vAKfb;j2ZSmiCj3Y^uw&Ip=kU?5G39Mr_SI{ei;&8B}_O;FOdCJ z!on#DH*}d&BF93|7v57lR?B-yH@Y>#4!_Vg=@~DPN9k?^Lb%fszvDk;c`2clX zBceFRTTtj9gdY#%DWH;N{y_*hoWm{X%niaF$Mg*%drSsM0{ti%$+REmu&252BLV}Lj(*U!#mfMya62AElm>I;dbdJg46g?mKvs(WokV84RAh?XH~ z4&^Aeh`7mVDwEk16H#nVM;TgILIiSlxud%!L|=aUOQB+T7|fON08x|>mPUt~nXx2| zsFzPD^)q8r6KqwYxMhA;?QY2XCZR9*Riqg_Ve##5ve=tM6PvT{iOjE*2x4;%SEJ2k z#9-Ugpn@_V<0wZQ>4&@GNav&*j)eVOaAbLY2#%aix5(a>5lI}5Ww~trZDJ&w<5@1d zdWRs`lH^8(f=b~-d%$2|H^ZXmGZ3K?G898%7>JG%f@do_n1SdkA$Z23A3bE|GKNE? zGR>4NQl^#$!6*(p@aL8>=ivdp01$yM$=^@m72yC{xXX zm;lMkR1ZVSAG)?o731ohaq2Xr@E|W!&4m=EWzKvscG7)SQks8Q7w238ph^Tk<@pbG zacUu@n-6(0zf3j16;<9P268U7q91n&0|L7~F15;x?!m#=EAuAoAa$`%SQ^3;$Qt}o zig`ufRpKZHM)4naFepBxen3jVX5cVtM0D&;wCFx@h9jt!nO76N*nxYBq3ga=rT672 zufUNEMBvg01YaZVUjln3!9M`Q0Fb^+Q2=whg8B=5uQkQyI!fB8Vw6-v7;~J9(SaI5 zUmY{R2f|*(sIZ0z;tYsD8nr~upbnVTl&hFNNIL_lq$1-usgEJ0TL>+W%ee_(JbIb! zqHQ*~k(+A?Bgr+arb+s7Dc==>b%$ZflxkvnkI}|r%{UJe%t)|q6Tsu73|C6C7SIA& z@vOp2&^-c~7iKI?%H)BAmC|j3Oi?qR@~VeKP8EE@!s5#zN>Ekk7GfHyE-nCe7DvIh zUj?K?tLuoNoEf#~dL3bE#0R3}V*I6GqY=^UsF3%UhBQMl9{N^C4AgCg;*JU`q)Bdp z4-VV{2Ic+$mJA@-_yE=ch|fPDoOCf4DN_J=0i!-rO+a-%AlBMrd8MO`v8+ss7+EP@ zEOs1af|~(L6C*em3yw}bB%IxELLrveORCud2U3{u?PnM3BkG`xH=S(QAHA1#pLR3^)$TRB;t7GiWR$=0Cg#Oh36vSM%dr zTG97jPr4sl;f#AeE(iA`9r>LZ!Et4(ILl(qs=<8(eU1Aj zrXh}s)|Un}Qx|aKsfa52bti0f6HP^AGnG~2Mw%Y zhnd{@5KiDrdV&~^Uq$r#t0=UAFd{LQo^cf|ZUEy1BfH)}j2+WI6o#q~ki)LR=SO<< zkId+#mdY}}jsX#HaIN4_em~Nao#enbne_^GvRGhhD-V)!)Oi9TsR*okD;HA9MQ>Ws z)JCEwW)9cYBZIPVr@&uX^5O23yvM9txZ%`RjH4JhZ1lQ^h(P9}uQXolN7~4cs7o^r z^N?B-T-X3x=O$t~j&qy9o1V!-&zcBF&T$^nc}xt!Y7#vrtTnI>RU(?H3_z@VOpL%r zSNWK*y%x@N+;FIJ<}M_(YTpY;iA8C;kiyRbGE8_jMU$ZnA8?1Uor z&xl#!&w;N%_A$;~7uT>T=WlOCxkdZtHxDtgXWyPd?p%m&SHieB(x7Qyn| z24l{~ zIamrvzWzBev?rbpl3#SexKq2M_MI}m406^#C+zu*y}gwB=;XjHT+bK8Lc6j6KK7B~ z%a=A6FkF~8;k*i-MDRRKE@CD! zMKdcCbKFfAf5x&6Y$41!^^Hi>LKtW?Jz^%q6fd|MOl%j& zy(ISFSgn-^$MK|Aq8DfGIkdEum?>an$8N%LaDe!B7QJnSmP0MQUlE>m%7%dic!!$e z^#i(&CGH7A;4b8)G_zAW*an3ug&byhAnFz2BI$7!7NJs+C_EltdeSpsO=1dw8GiNQ zu_-YxQA26eZ5?M|-D12{ZOxDbC54A(c1{3?CzJ&DwxzPBkzDsc?y6oQCBvS;Ff5$D z1_gknTSa3xxQ@yb<;fX zV9wTUfxWM7av%>g%J~d+Q)}_2IA7t2M~@k72+G#_0|1vRQ^mqT&p?XN2a{q!cVSo# z7ZTkwUt1wej}c50z()z&a*;4*=I?1FM(QI}?uH#3@rBB^)8M^rg+UI!g%&uMQXkCk zyMGYl;}hhpYnaw71R`iO9Sbq+GyXwlU87>s4ta)bdLfsA^hcxEnJ&T%Xh{(O=VPD6YXYJO|DrrVLz+GJSXOIIxE1H=Y zy_6H$oZ4E{AtwTGJoF8bhT}VL2p1fyyd}JF?E99m(W=FJr0WF5wN&T=Y~@>+pdikC zOKj#e)yiz&5el|03nZbnzA(z^zS8aZhJ4xy!+@nn7=22emA)eU#il5trvV~c2MU7D zK*oMjT^=-Dswo7J`$*m*U8$}EBz;Nc$q42);R1hs^yW5yLjae8C^drPy*+kQMd}zC4&L z6!=01?jJf(-Y3Fd@GmO#`4eI7r4A1uXq^!FypCeAZnf=%#Vmc4B{!v|U9s>7OiZC+ z^dCb3eeWy23R?oZ;@^bTgzu z<*;%pFMxpNu>>Q^|3P$3NyIo9l$S{^u<&@O&gq1u)X=e7AWL(?vM}G2Vt#U`4@`ez zItLRle3>mIAEu`OILj>1k-AA!80m{)^NB_ND7GkSRxhIU(zqLr_woTpu+#xK@{XG& zYS0uYo=S1*Kt*2&GePL=7#?35p(72^|C-{^f!e zQJHEpeDPSJf08KlFG5dp8}pGuFL1dJco;gXMDK8UNN; zxXuUq9Hu7lg{v3*#B{Epd2HXDc)i<@wd2IR^to@s@W0EWi?FFBl4t@zSJP zU<6BmhziJzcgN@tAip#q6C?}tQjFoS1RD;(G$)OzqQ$V9tt(8_#flzt2ibimJSM(S zDh-;@8#^plU=AyQ0~9aV44HhmXi~H}0tZ?bic#p=2HQ=a#eaa zhymB<#}kCG_TYY^DOL36cVeK{TjrSu%<9W^>0r*TAB3s)8C*;4d!`n-PC?-vWd8#$ z(OxPagaDVafLs=bo%A=e+ucmQ3R086oS+5SSR<mRL-Uc}~%KbqDCi#$76lhd{7+R2w666I`L`guu0FfKzf5KQNjQs#T z8i}#+Qrph{q%aK@HO zzlA0M$F}<@_9qd?NmfCXKM5n`*G1@0H^*(gsh_|Kr~6!IDu&&9003bV%xu`#fGsc^ zc3yniY~=<(A=X!jbrfRk1H$hC{^-x>f|({`+U~z*3dFSK<3x61CyM5h#=K>qOiCAW ztGJ{U2jZh#(u}jL6P0sGH-jz8uA&1$FCaKo60dfGa3l%q@ zH7t4msHiX|rC0(k|9qOi107K%`*EVbqe@ls0J+f;UKG2JTi293 z%HLo6!K0>3a4jCzlEslQ%Wyy4OMX3*pKM3HrsLC_(;09(2Nhi@|- zDWcbO117@dFjHi1H(-NpH((+FV5-D=q{mxfQxej=UU=iNJx`%P7uCp~oTTrlT#a<% zY`u-T)W|-L@h~!^zW5DQN+C~h&X)`RFA*JuD~0*e59R`JXNh$%{;ndNo_rgHs*{V< zlR!1_V8aC1RjQLFoC{mg8+Fp2bE^QEXpp0H8PTBA+sbGDkH-P$1e z##+>_O$OV(>CIwImTE*Zd()ccK#^jx8!aapeXthI>_HCWlp%Do z2YJe98E8E|9tK9d6sK5~@X5!k^x!9GK~HEh)f?^YNxBYLtaSV1eKuyMjR|W&H_CF| z_drnY_+#|FCpkvXzMEc=D5TcdMn)yD!|9mwXo?OwfNy&jv{b@@cPCbN(Jmd*oIeUf zN;y)`%Yk=Mi4G~UT?hRN*8|qeaHM~CHI9T|l5nJ>j4WTkbOqM~>%OC*x@5Z1U@X#8 zn8cML1^#ZzwMO(xmlPXCW60*;1hak<*fyf2dSo9?xG~zN2W^%Zqd)b?SdP9ivh77q zx@if>ygu%LkyW)u<66%AkcNwDL z`Y@$wHlca?WUSF%%>B^i*tIr?`|wgK74jt5};{zAxVD+Y0Khc#f{8o#UhTlNbN(FX(4 za-cn2IKW7gMil<)6s_pH=%ZVTP@NyAsspOw~@aI={9(5 z6*E4wg-{?$A+l=Ig+T%8`310T;{qTuGC`qm_Qy=o19Z-WjFsSwVZSs+=qPwChjj|cQrJ&GMcr)mfDa(#UG!Oi zGqo#3%&t((2tV{5)NPvh>KH*LV!Dsj=)5TzXv-)EUIhiTf0NZz-#4bN^i1%d~74kc*A*A2c7n7o58fcp_UP|4Q5Toh?e8( z-rnz){g>WS?tl|PA;K@osp79AI6}d?dr&LWDk|bIGz=)gWd^;9n`7*p7|Agb4RjDk9~(gTGh~P&i2cdHB~no&qZiXekG40AH$qRZgb& zK+yQtpyT*voceognpC0n=464yBn@`YvO;ROj&v}rNw`mG$6qjg2F94=7dVwNbjE+f z)PB*KF)+LbqAP#VS^f=s_6ueuRRI~xeBgmM3@-deOm?FWDdhCXy8BC4*hUjnX=B+l zOH!T9$zF%v_9c7GOoY)T$IF92Hk=UA6W1~HWy9UDaM06rc!a`SL;)SW;0|o9nL!W% zzE&xV8t(b)$;APT7XcpR0FpJHY%%kW;Ub2+-9fYak&+P`m3v!CMLt8S`xz7}_2B~Z zILePm9|^b8!r9d}Wqy3`444XwSzXxEUKmDcV#@G%48;rU>|Es7pETqQOF<$1VG%xX z0G8#+7&CD%THT-QEs(-VGdkVRc04{JtWig&`jb;R!FQ0B6=}|~x`Q08NK@NDU^+Ir zcoamiX7$*DBg^aQIMUbMj3Z(0RvhW9--=dRk#b2q2!v-$T$0hV>J)(B&M8x!0jXMO zD!lrD)D;l5Omz;V${E-KNMTx-Eb~`E{EG|LahYl&K$KjTLFx@t0#hluBtfbY!${S2 zkWB1CZ>&jI{+oS_fpWkVije&PQcwHZZASTW{49S&B;B_^iX1@pbDW?AOS!2TPZ__U z%HKX3nB{md4LHy){~$tV29Uitm;0g80i>z%r&|oy|0<5X@2K@B5y9IH2O@kWfRefh zL$_$y0D-yx1Pw*ur~RjQ;_}SNCSVt!ejjVVPJTJ14?rD=V?*}kT)%**C|2AiqKC@P?qc89GN{BlPcPz&cC5ge%bbg!Lgp$(~rR1}1n#zw3gzB{aB+Q?1; z7vCGZ&`1U0H}H>na7@<3Xz;^z`y1WwCmO^1DHeG2ZzbwM4i63yy=xvSP=GfY6H{sU zE23vYaS)ZBFb{oD5T=}=^LA>J#zLk#0kd~=HvvXFy-58*JD>#IJ|vpL)T9*}2HOZ@ zdW8nF;!yBv6kohl_fy`$VDH~&w)V~qq@51L_@ofdqx^WO)nYQI0}!`JGfbe6t5751 zr4J9r8N9y1m@d*!17qNMkC4tQN2A!Ju_gnk2H%n)onDT_Y|_H(!W`^NUj~%$Qh$vW z)51dlgVx9=%x>@mfNyfiD!@|=mC_YUh3kmum2*%Xo6L~BoUKqKt#M;f#fh3!I~7q& zPlk=DG@=&DhS7Hb3(UaVvF;ieGA2w6mugWV&GfV~APZ@>E=u&J$`e@M24;Yf#QXRP z@Hs1tIVEtEiiI|VFB@aJ-7*RD8LqPmX%*DMA^X_;chOUG84^n6KTGP&M~*7w5d2tg zt_mDid(TBjRY+Um2W6x8Py`jprOiswa~0BZh<^eYR0+&*^OS2@IPC%uXpdvz?`z^R zRqQ}Mw@W!yW6+N=G5u8}&k zINC@ukX-bSZ0A5Slue;gcI0sF*yYd+zhyAw@MApss~F|j!7LZB9F^L^w%BMnYPTZ? z^S^<3pxm7_*2pTC< zt%X#-12|O=DINoBf)u8uSBt@khuhkeh-+~Vse^+0X*hw=AiR~NyCwIzy?#R1sfwk9uM6G zkQ1Z_K^o@66wkUhjWDYV*12JTXI;B8Hvn>uiu9OfF!*d-K3t*m=bJ{P>qPdq`8yB8 z9$#k&QXjyK4y4kEW`>%IsOT;*(}^@OQC7JR(|A^P*Ov}CDYvq{5oJ4(&gy^jM9oex z4FmiyCo;m~Z}{)JZbI{MT_Lql2WGI)8BVnz+)-!J{x>6lKXrzg6=K4L^y$Z3+8jTN zZL!P`J11FX>uE4@NU-9Q;G}M3w98~E-_S0mrPEaPfxT$uRE9uvhfy@R-`dUnnOQA?tlH$B5K+jy^ z2zR3Zsk)J~*kjRrH`0^c3!Qc&$8)Y-K|FVIHQNiVcZXy1?k4oaowOI!ox+Fk@OTzn z8_|zWBI6YID%5)M_yUTK^SC2Cd3{zhBeLjX6hlhtLiv^<7UZgcH_0Wq9V6R6e-lVGyE|e(1PF(zSnjVPDs4-m- zg3l{TpCzLnOC*5;hD-rHDu7=Wz{5p;ExW}>QI$8@kG&Ut^Cl1IVKT9vG}t!I@s@j$ z82ov7$wH@wk_LK*Vd{(UpV$A|cgmZj`=X(LB14QB4Mqb3R4biiDIfZ~fgksx3qj;S_IcDCL{=$P z?TzwAlB3wmkxDS>#FiqrVDNIzJjV2zrW(_0%Kq|0*}RR-C__TdOXaN;DdqLaTV-oX%{qn3~Ao41nd&_ zrb>E=QdsE-;qj!zp3)Ikvwh5T%97|>ls<-xVk7i&3~8xU{|+P&LF0!YYZ2McqY*TN zZG#!fPTgFXUq{2c6S%TdvLCnuZ&k3rIocK$dZw`kC{0A#IWGbR&{jqV&?wplNExy4 z)L;r3Kq}t>;9KBu<_B~l^j1XnSBgU+!%#3pQxp(N+L_2euZ%;7`9ZC8l5NUL9_*^Y zp2F#UC?gascLQn)C5?niHEqMvCkVr7F(SiA6ZQerH;lAkuRuXzq%~WF7KedD=;uL{ z98C5^4Phjo)6|{$jx$%gGsfX$AND#lG@SI+ISum*E73l)Gg_gGH(bmm`Oa|CgY)qK zstzZIv8N!Nv81`@a4`5^O@f!6i98RJT%cd2l(uOS({x7if@w?|cF=0f9(=(tv~Vn0 zt21)yJTu3^W`i6Yomq|!LmZukI5^72MUVpKcXk)xJ!Bq)(m!R3qF{~jw2z-ZH`dX9 z`igk5qqF_=`7>he6Jw{&jhnw{W~}|Z88aux&X~1${=9)pW-W+aWIs(jYtD?xqh`$$ zFSEC^L(ihg>2S+1#sQc@&~_9mGP>ip5H~9C|9}t*X&htNKf|6uvb1sJ z3bx_(IM_l#I1k}+cZ}0jaWc~hWF6=KEU}%;brRW=EgLnNtpC5a-_B~m^vQE)F0z}> zVi~R%Dx5chy+Y^!r;Hlpze1Wz2^Bpkv~4O`z^#TY^j7V8vg^}G5r@-JgM_hU3r}sl zP_b|lY{H+FsrHl!;>ZZDWb^>#{sjgp_IfdY{~uuzro#WJ;tOYmio!F03wNnVsJI8= z(M6%+JA^_AJ0VQY7b?`RD8u~Au*;Oe3uXw$dGLPY64)Tb?T}v$w4ZXpRL&`b`vuH< zYtJELMK^?s`4GYjg^H^Mu#jIBD$K756%(iaU3=Yip@I)Itq>|8bZhE&)Or}+Hy?q8 z9)d1}t=b~fY=HeDz?0xF1%t{d}Qf5rm!dpx+@hL3-0R=vxRHz{3H;2ndTI z3|=Hu6fYJkro!FJ@k9{P1QLfKNbR8T9HC;~LO2*fI0)g&LYen`ayT3R8w~vRki|h; z7m&Txo7kO9to$B>w87CGJRME_-~Sc}3U>b&uj4%U2TC;y`?YGs z2DzSh*elpHd~f=~kF%F|74)07c9uBs#68zRV?!2wSURZWb(HMIzQ*yXcMefS#z9rn z_f3@4&(R4PWo5GH={Jvtk@~SiWv(lEgAdw#9e!-OS&fRu!+tY9I7feaG|*;$YH;DX z7Z-PDibrn~`aCGKaVp)eotNT$;g4tQCHHR|KDJmBy0-mVjg5A1`Q7i^-_N{1xvh4~ zqgCoVEYm6m(ioo_VJe z#}6z{d%bmos{R(Y>5^xwF2q~Ed8eA}6fEp}>StIVw!?uV%jSOler@lbT>me+^S9jH z_4LCFj(x8U6(@PJij!(iY)ylIXi2MEOQ+R9OA~agUw_DM(+)dHdPiS=yd!kL3Xka( z$yq7K+J=|6F1bE@>}S`JB`Zp%=sQlYzx?5;aaqKyu|aze)jtcL5vO3E$$5X|U8kqN zulCfq{1)R$pVb~lE^S~LyuQ#M6?3Ma6rc@_o+b_fS!t7JmymVe~yw~r`8W%Jo?#^&QmHPKZKSmVh6sEC^mM=5(eyZD7Jg_$9 zr}3=#WA%>Hdl*=hc`f@~^-v~j(Wt3hnQ+o7*)HzOS(EC6oJNfe6=&5Ts6)VMJ_Jn& zS`Y*fgb=hL^nlP4f(`^-2zs)Lv%NsGM*o9mb9;ei08L zI0<`Gn&S=iw*7hWyrq53yB#eR5y)ER)#^-tPHvYwWwfyp2vzKck(| zJK^Ffmc_jK5_`T%?bth)&8)Y3MSG+0^giCV)C)h)VQIX2X_4rcKVVMUJL`_kd+Pcj zb=SvpZJcgBy!Gs+`a`q1`(qPauZ)vse?M<`_woB@T`P~|X-we^%GgkG0c`MszO3Sc zffHNZ__wCI|IgVa15nc=f$3p)-yH^7mi*x72bV0V?Dytc&yAIe!i6v9hwNV+)g#91 z?BU?A6+iEMv6_}}-b*!_O4c~(7yRhsJn!N4p@owUEdP9bk7?|JixuDZEPHNJH&C&q zb8tqh{m%FgFN1T*WU^ZA%R9AAE!(_2e>S;R{kYlxOU;5B$49P3IkBPrC9^s$7sS=s zRLwbl*z!bO*tLwGW#wy^X}3+iQE1jY@}NwMJl%0>+uQkPT379V=8$kHG*y;#dz@g1 zGqvP~=<(?E?JJ9SZE)?%eiZw9+r1I$S1Qh5jn#=h7Sde_dX6pYxD|uGJpz*DKGZVN5>9a*M1Y539%Y-|ET7>UnWLeo05_h#I36`@Wq% zna>_Er1f);6WO&Zh6$c~n~Gh{yK-yK9_shtb=i{yzg~9LPl>aKt;@a{U06EyiOBb; z&c6HfsB0_a7ydZ#B}0+8e2n(p)R?F1gL-~>A}*gXdY5ItuWRj=iu@-J@VUBr$_H6j z{E!zDzr5HJVxK!Q+CB59Bwg@h$xQo4KAYe7OHJFn8m+H7Xyx+Z(ZTp=wU>hv$Az`M z>X~3yF7iKr;IQ|CBiD;Zt%y|pG0#-d(711jc0#e9-=T!l_4>`H+=m!WV?Q+SG4=IR zC#z#4zw&l^gl+U)cSoBRsds7EY+ZWd{MJ;1gA=AqzTok`!s?O!O0z=ChDDszUb2b; z11B4Gvwy3n6{{ywdp0@h%nI)rb>Zv2Mw;xZzmZ`UXFqN<6?>E)&H`OzI`d@aVSHd#sDCJdd|DRqe`Ts-aI z{KLa;e)zcH{m%l+ERE9AlZ7|7cpYmPe6{7s=aT5p=Dp;Z51L1XAJO-6d2%RdTF2?Q zLu=n!Sl%~|x@^$mVQt*=YAdn2=j>do(np5|&r-38LKiOe3KSb;u6|QqJ@$O`@hNso zs|U`!wZhOQyld3!0{PXBP|iN9DNR3f1iYs=;ljaY)|r%N@~02EzG|2Rceegrr?n zzu%yGckTE0QIkUyv3D1pD+qitdB(SmAqPs<%&{Ih?_{rgbHWA*7M^`GXZ6rSyh}B& zcmDb5sqbjN0@{R`lRx56kxAie=A! zG&pgeZ|pPiI;vc6_t12v>fMt!504S~o#~MN%$qpr(f4D{2`As6)qy%2GF<1!c;5}O zJ~r%4iB{Nlm%sy|k98+ayn7}7?AzoBoe8Jrw6?1&b}zSGnE!DRr*f~XLeCJ?WAShG z_+s^>%po1dvR7&bXZQCrkv~)~R;%wQOR2tn^xUVb*E@?}X~02Y$)*RFUe_iDYD%xH zG|lI|ebm>+@r~oe$`^G9M0GCb^zOupF4<0ban<64RqfK)2B+a{!SiDl9~Ngk-QTWq ze&I9Kr1e*&Lsd0`^Y44Re2CUYr+Al^?lliucxb-Ftk7@Pd6&n1}`MwP%`}WpcTr&CKn)#;{Q}(0`ZusnY`o;uy$NZTlvlnexP-RbS@a5y&RL5+1*|@LRL#u@{k(#IMgjPTlRjC3W!XcegL}IWg~;$@}T!Gevdr zMq3_pM-`m=^s?*7VwGVVsw~MLE#rl`FUKde_G!$BQ;%&e7vJCBx3|>v>7ICZDQCue5%6TsC|5mb#I(ew+?#S;Zo(9;<(==Kxj@kt%KB zoRMYSWBV?vJkdGWPB;9z@O0_smbjeA0AiuewZGyOSq~lFtqwi7&e(Jkcd6?R&kOJ7 z8|<>%(;Ru`z4LHm^MtX<){zzWmdxaZg+(oi?D6QO{~p$vfO`8`*+*up%#ob`ORM)Q zy633xrw0Y99@{?H0L9B&Q_9EB>47##)^X#?ig|ux>sKCmYCgP=?r`#U&x=O+NsleX z+%MZ!_3K%@i`c2Sb+k{`-1*xk4;{pMdMRW#>&9qt+SL78PmMctdL*np7U8-*I^(vK zv|McL*(IO2YWRn!y`cm5%@ikGIh1NrL)HD_V<*!){P6<9r!7iK)+9(q%-@7cH*ogWuX$1R${v06X$;IesV{N!$5H(z^S zxnPQs{^DyJ9w081q~)J39X(9r zl+}V- zdbIyh94b==_DixYQx?m27mSMQ6)KfTYM^7mW1j&n!hSY&T|J@-={ zp|N<@&Hz>^7HQv)rdu{CQ3OkU{#?w5(?9^|3>~ZQtf}@2{5m zv%mGc*&(=__Py$e-N9q@&oV*Yj{MA;>r+mh+T|M8`ctf#eMRHS9OFOEZs*)}kX8J^ z>aqW~daR5n9&$p3Ag>1>Vl#*tZf!iLgXU~eC zV5m`VofTtTSbA!4)dbhGUAJ1V-s;`Onjh1<(D+v3#|3HDe;&DEKCB>h;d;$|@4oZK z1^$&)vT*m*=FjFmsV*I+b(pPW~`l1W8Bz!X2@&PtKWPp z>sQtu99Hsa>xit>s+N(RlEUd$Z=T-C8{cni<;|>ZW^?!2=9VmY)~=O1@%@=Z_oBT) z&o*Y12WgBiYb#D$@*`mU>p%O~5%SS%RH}?>g1lTdm>JCRUYXpg<^O30zjnsze(&2( zZyp;u{Gt3>)A3ia*DPJ^;@m9FTeUBJTK!_G=g~zwf5b;wOcUJ5vnoll{rQy>Xt$v` z+6aQNtU21G>VT%xe`~7hn+e!b&bzn?^D3^WXnl`Mb?Vu6-Q4-X*-4{n{9UdsJQ#c7 z^9_fzKl}-&r?$FcX~AZn_R&9E?_J3#a&Atu(tVayl(WKWL%^`bLu$5-bLiYO_{a)V zV-M>GIzsI+FP0xPS(=$%sutDztz+!N-UlpZPCj%qw#7dtZ^(YF4aZ-vw#s&CS?}+?69=RV11c*F*uBnO9N;rU)m&%m zpS35+@Ao@|-_9@6?WMKrNN(&+tH&cUHvM&<^W1pDs!kKxs!r3|L7FcAt)&G6dxMr7 zch7BK%|DklujY?ODJ%AQ91C(S-L+cur^c+T2w8L+xXLbf1be~ zo44Ff+F$$ntI5^CEX&2K|KEP9%1eI?UjIKK>&|b#&SRp>&MxhF-@!1yOX$mC$73;5 zVmJHC+Vsb2a&Ye~f0q6!E;EY39hi?hQx7%qtzPEa#achxAmo9n%&vn48y~zD=tAXWxq6(CjxVl^OE2VxB%)&ycLAl3$A9U#^P zVm%<%2Vw&tHUwfLAT|bK6CgGPVlyB%2Vx5#wgh4;AhzBvV_@^-G?xWDR-k?3KQ_Q9 zsb6-}^=j~GzS*a%Tu&|7zfXSs)#clru9tu6H2l7N@3mb=XA5gwJnB?ok#VP7igszSZ{X`Xrq`gUwrhWCeYhWvv+@ zT5!vGwMI~_dhcVU)oIO=TZ|ZHamBi1@i)lLy2w=D+ZJ)l?0xmBl~1^W_uNsfU`;8^ zP#5^pF?ZUNR4yrfmh@`t10Q+rEuJ|mv$=Vf>r>s?VmDM>;+DR-abw+wAYZ%BO4FGG z`c~+BJbk$66`vUQLiw4SRI2r5wsVMnJ@;t>ryB6sqMAFiUq6=jsAE~_8M$-Gwe`Gf zzWQ%{cvUD|jhAU(bNiWpHb86(#CF@y{Id^W6yZ-({defq11-C#Y*bWHLuECGE+Y;j_gfHjY;;B$l=Jig-TDBu}Ue=v=dI z?uB#u<1cRF&SqV;c+lM6cE{UK_z9Q(( ztml$}i=s56R0b)z#p~{vXK+eGxmy3OjtkcAH+h7{kmebS=jJl(H(mKQXH zj~&PI(#CI{8nHh`H};2RTI;^ERSvNl6K<6(?ANeq6Z8A>4yQr)_qM;*n480&YPqfa zK~jr!{Wt#l`}Xe5DYh}#tBbK;Vkg?vXk7f!qH1KZ#PVtulgnq%cyVg;2|Zh;&YHi3 z#rh>Id8Q%Ln<=>|%n6xW6mMf|Drw*JaoPkG{NrCr9@2NFX~wG^jmjmP4Vne~^dbD* zC0jm!bP7Ebd|5kWM%((Bu*T=nekrnr&k9eArYNqaUvU2rc;Rc}nBtJc(kX)jEbdJY z+hhD7&16A`U}NupLv~Mk_#kKA!;JAuXTSfFdbG)uQ&{`mxk>Wr_Q-j;bHYMYro6$Y zsjJK0EDNs+ow8xaS79A{b3*#CJr^FFN&n81jelPrVKSp?+0jkKiRb_2j~s7pE;Tes z2n+8i9k%RP@SZy#vO86_C$|S2J z13#DCaJ+r1c5rx6wK8_0@uPcw_LZ-0W}MwJuD5wCd-~8x8I}gU$ZBJzIdd!7Y|I>` zX>V_G9FzNZXW~rFZWiw}8JqN&HuC8v%xyTyO~GyD&Q{F2XF6;DlJZ|F$pV!;6+UEk z;EWfh4_BVjOnJU=!hsr<%c+hU;}x}o!Y}d8gtIM@BJ65Lw%^JfxbEeKyF)(SlUH3MbVhNDsO$RvQ_-xX5U#+uP@e) z)yLCYIv^KS@)Nmn18e}R|5payt*SgwxuHA@g&(N!l4uAI3v$~8qg53LS~}}1o7!P6?|&bS<-*ROnLdcRX+zK?55xHs+8_LSbN zofb#5uSnWXyZQSR*XZ}oGu_*#ypk<#KXjzbtyXYXLlV_v&i%5G@lzDLtrx#DHIM5z zZS3F~k&hIOVXm_bUtICB*%rIYIP&O#72(J9Ek0W^_a7|e_O2Ws_0N%AJ$md3n|4H! z{UYbY{LM~N4-^lYb7##t{p;b~hPY1sv3zck_u?mKk3Zw8SYC`3)tfaSu;ta8H9f!S ztp9Iet;4LCY^0a#^$aDeJg9~2hXbOL_^Rb*0YNW;1O}~FPXm{`^ zkNi2Am|MN;yV5E360_0I)2vH(jZ8i__`>&!y^lLivqyMZPK`14Ub7|qSo-Bp_ubwe z@%d7ECTfmg(XN#9`}><;!12vq9H(ack=kcVCjQvRGduf`F{wKD-4V&F#S6|)uJvV1 zYd78+WGUNxt@Aufx%kY2g#%tBe*H3gb)SFiOD^c}=O5*7>^I@DvnqYnAm?G(CZQpV z16H1L*534RaP@)}k(nd!J`qJm6i$yHKcAfx&-+Jf)A`#uq3hFM5WUVVx$|i3w$$J! z?CINN7v{%Q_$6otJ(@IbmG+`S}n|S=%P~(oP+7IquaD#P$QQ!Gf zH7t|Cu$>V;1W60QhC$GQK+ozEdOYUlT#UoAw|^v}io zoVmC8D$0HogF)EYYU-)Z`vFSUWd@tU5UqqF5h@osX#>)dUh=)USS_o+JK(8?aHaFP zOtBvELN>uLsu;nf^Gz6Hjqj=k#Sjz3i+~zz&R~3xQ#EuzIeCP{?7Mjbo-E9e&{=(( zd`4nUEGX!fhgoy>_%RsqMUeB%!%~fOLl_L{WsXb-;djU)4GANta4HY$!wPsrHsoOz zdhHMRDs_2Rv0QfxcAm}h)+DR9WA&25;fMsE`liWXP#;G!7*PV17l#48nO6$E@r7;u@8>E=T-Gq*KOMMQ}t408|5IvsUsFX(J1B`m%4y;ieU_GTEfUT!W$wyxvqyB0|H1Sdv z!bvK~0PKQ2paKYzpIc|8k{RWpuTEYaxpEh#+btNX6tXgGDZW0LzYB|D1+(Sfc40nD zb8n<gE)qgcI5~YcHi#KV9@>osCjF|#^QSBE z{;H&>QXWKORfMX&2&09r%#$Vk0Dkk?4OS}RWq}{ClT?w9qUZuOED7;0eQPKRW0S)Q zFgF&&3kxt;T`SNnd@i4Q^h2c*3Q2hZ=D^G#8wxNZEYFa^NQfuD7ht|UP!Gk9(kgZV zO68-EwBaXY$WePRQ{G>|?+7?=59VWqfQ4*cwic7YNM(o^e3(uQMnfljtW?yuf#mHy zSa(UoK)MgV5q6oVM2rAHQ2Z&dqHA)YKmY|t{}iZ6)&R*_P_Ri|K>6tX)nFzAs%1jy z-2YvT2?gs0K*`uYC3H(xsHI&{5b~#hZs|CpRRaai1IZnGvF<$#x>^+LgYy2w;8P4U z4a7s<+>2S6n?VLNixyWQTO3IpWC5U^oiUW!MQZQEY%OQ0r;SvlnV~UCtOFyP%A#(; zR6uj7f*i3AGh>xHlT-I$KK+|Cz&gaG4iQp)!7@oNb5&w$bFg@LVjZIq$E++3La=p-jx7JOZFr zCzGfU>+q~(2~*=_1vILdJldw0x=Y&x*i!hkC!;xYemKlYiJfEI3v6=qt2EK|BBh2tnhG% zgkLrniUWo)+oa#D#LvjZg_t?BoZJeLBmz(?qIZp?sROlLqkg6G1;0~qbV?S@k#;aM zjPu7pR@%YJ;O38pRCaBKhO`PMb9736!rzD{Dfwf&Fm+P$WBv!K%xFA~Dgf&Ry(nih zpo_`s+J8Yr{|k~ood!T#9;L*~V$cajEU-n~|8y`V{~stzA@dJlCahVv$ejl;Q&z?; z^4tN;-)RYGt5R2x6T(Sl3P5}LFb&X{hj3^Uii}RF5e!fD5VF@ntd}I33q@rO>`E3> zwvJ&1b~J>a^#UF82!4^hF(36-ZFvDnmWeRB0J3N z9#<2J*g&TQtQLc~N!%}cuX*Qnw%@4?9R)xb3`M{XL~4k%lrQ}jAby-P`Ew$zN65f( zsC92uus*ahtW==2nKzjPofc4w01jg5k_m^p0gdImGsvPt*l9^wBbz#)go+!jG^|wa zwOStuItBAJacola1}H*GEdXYzEXV-98WjYm>Qwzkj!R5xp8PlR=>LIy4Ke;3IRTKp zX=Dj-&J`6x=fDil1G7z~Q8c#-O#x_kJuG%tD`Z9^ARRre+?;qBBg|Qe@PHqgm`~)X z(_A8N5dgDmiQ_9FR%9-NudNl+B!in1sG3%4POQuXNMIsClNt+ylaJ^VWbzTrl}|T( z`*l~tBO1uvN3aOTd1z|e45D>P*$I4tiY)-EnGnxATSGG&wKAlMI;%~Z9L0K?#sNJU z3+?_i2v$zWXPM`A1QJkfWF(a1>dXsrE@UL?%*_rEe8}4rBGes>s?3nCpr(;Yuw-mH zU&&zpN(C?(zme=<6eWQ4S0#|}pFETgR{hWXv)_4%np5`Q6@YUVO#~ z_l$LDT~}x#elsDh&>VxH>rrYvf%yb zFWRNon1q4M!l?@+WOu=Yf6#=SUoei$NYp@HJcilZq4s4AJ0x5%zzhv>pDvleu*hb4 zg17~gKdF5j^Cl0S!1N?pAf4>ejBzrZ=}>dmEchsg@Xih*I+oRD=wx5bz@^`q;yy|i z(O#Ut9K`CUmc4|X^_md?+a--~u~NpRfh(CFr5;y1BN{_97dJA*#RN@rH=-%MY_6Vx z3Qe?g;4w-W3Qcx^l@>B8kj!WxpPaei>Wo0zTpP#~vnBA74q6|BQ z=CTHoc?RnjF%JMlmCS-Y8zyp90AaKU00j)l2rMXkda0f*xV9jMiL26Gp#1duRHnCdPv z>8qzENuBecr$h56x-*Ege43f)I*(c+`Y{m#yY|O4pF{@{Qctw@L6Ca0=(;EYrFu?g zoyGdQse00m=o!xAE9>Hs7~wP>ijW~lQfqS0fZ$X%i74MHveH0s;{7;LsJd>PdK zR0g|_cJt9sy7K;^1!_AROnR0!CMZw9PKVm=g#{MhxR10WKb*(RgAw_F3^wdPdm~cCP*rpa7!h4ZsqRswXZ7Oxne9p<> z5JpvisMHRW776Of!V6f>N$a5VGA{%M71Dy0IY$7y1q98Bfdnc*`!Dw0YShFJ5Z$y& zXITW=P!lW10Bu=>A+d4>6fq|4k{-AOMdYZ#$&OXo|CuAz(hc-U5@3C zIu4w`@Jfc!{YN=vVE$>!dc9|7?MILb(i)FILUm^Eh!NOP>1IX4Zm>t)> zk*^Ak0^2D9h;=1qYjzp-S0Ha$biFdMGRTTaBsy!vvK2uNH~W+0D=`z^uPP1T@etkY zFJDoKJ!Z1@HOMDdVc|^HS|QNW74k% z6HKB&{Aiog05KdQVC8Ot)L|G&g(fOH)eoiKLJCcFg=Q-d})80LYoADgl$OBEEr3?M}J1p&EBSP$_HF z1pR|EMh?5@uVaS#a2|V^EWL}FXd|-dkl=Hfd~p{GXK{q2%RQ_>g1m<6CCvo-unaLT zFi(*Mm=Z>eCOFL%u;GiQFj#o$TjzY*D=FrrNavZTV8gCH)ij?0wo+skKafPnnxsg- zt9Z-yfQ^O46sr4&_(VmCI1r_3s*D&I;K+vB9imx5IJN2G~aB*H+ka$ z=HREi?A{wC@B5If+L z3BoLU!!6(fbvWO_L0qAUnqXK#XK6P7I&#v3=Pg^z71=hC7LPD!^a>zN|W?|jF|+KUO>~v0r-QRXI963E`u@002=jZ6HtXv1|!() z1m*!iGC(K~V}t9ZO+e69sO1;PO^-oo!C~F-7#p3eISM9r0MLLPOu&zvxF6f%GMjEF zg4#JkIObHdh@c-iSs4^GmK6X^(mWIE!yuVLoCt+TCID?;9}KCiGH3V_H>LB;8Di6I z9f{PyZ_p2DDenn3EW80ahni7jHFc$w`ctnWK@jUv89~EgYuGi9c=7-fwJsy4!%7-O zTehwV_A5`pg%4z7pJMY-T>BLKtB*BFjc1q#5>x+Y*gzzdgx!4y)gzU+A&~nOMspu&oZ9MAlRE2 zd_9$D6&q%cE}7GSjnhUm2n3Hh+tz^jIz9v~laSPae+PT6yA z3PBe!SJm7k6P{x>h$qQ&%oTO!*mG=|O*>SaV?_9)xU7YnlJ!~zcF~^!D=(GyQ!Uh( z7nqICTj(8KnpFlJUj71`iPZ7y3(U)oHZK7tJbC@pPe{t(*7Dtqq>*km?U4)fq0Dd- zREccFrV3d=mnZ+2;$wtbm@62D<|A!3P)SaVs_eNj@u}xVP+<+&}<;F8F5QZ2mBIaz(ZIQnlSh7 zNO)a#5qSe)lT|LWfNm5vVS^2jt;lNZgh{D&UF$nxeg>HCXu{mMsDI$L3NS2yOB%12 z*lbtWFe*a0$d7ilxj(2hm|EC7f(r^f7+DqcC?l_mF3i)SmAA>dmzXoo{{TDB+)*U{ z3Uf6L1ClUv80du3btK{Eh(9AnN4>(Vd$+?bFTES6gn%m5LQXg|#{`A~mEooz_h{QG z^@q!(&9AV2=!)t3E115Xnk4r%wiM;py~ehnxDSO1QJhI(-C1i-kXtBh3XkrN0r;Qd z;YiZs1j%d0+>t&EX~z6q)qMq~&|X*S?+1J`#0J0@7|Q^Tw1FvYXZ9$Sma^#SLmq0z z`b#{IgVt6G1VRzICZm#JU7;(0nzjXSn1q;<(M_%N&8C3bm1IE@x-N`_`71L*HNhon zt8Qr++b4{@sJlQ-&;STV38|;YXu&GAzE*%Eg^|JhuHLtkxxZ?GW}kMSUP`ohl^Mu6ggTWp#U8*bN}v%)xtP=P13O)N#*>mrp0nq5|~*HA1? z1^}w3&_v9^U_pwO2b~J&f)xZq;@6T|3g$GL?gHowZ14sAq$$76Op+{suO4iVN5}<# z`lw2k^$Lstcr9$_ErsMvF)O(BBJD`@S$4AtnwFY8Zp9EAj znw*fD1aSP)>)-gdm_UL!^Qp+tg#kmUJK`zFnM#&NDer74;dcdHh^ zqLSK`(WR6dBL9}VDjM)=1%&xsy1RaN5k6(CZo8ZKGPDMhJrdfDB*b-sRE~PT0Nqz= z`)Hy%q}w~pRCgN;UW`Yr(BrDiqVNG3`wsJv90pkactDZqB*Pb$X1>ge3vQJ_!o2{+ z%zB?|i{8S$f>$fdO4{Q>Il^#(Uod|cR4!{^rNUuV1eH^fue7HCHUKKGTqvS$)2pf# zLe<_fBMyi|rXv85&oEA!k<2I#l7R#r z`IPf=NT^4U)gLiE>o6!08!I$XjL0c8M|7o>x-yM|5nss8kJum=1O$JC3A>n1$>bwe#VlRR){XRBH08}sR_Ko{v(hBwnT;Q4#4OAg{=RAx$*uN z7HQmuIr?7`z-j6#Twqf>W095L1Z!c=2wS$asZ}-m2iUF1NGfeG6yIu7!A22OhDq70 zH(!)Fxu^|`(w`0ue*J)E{In1bH%W8)7qX!ZGn$Ox#4~V%!eZ~cm@DRIHKNU<&nGdK zlZ6Tcr5*0#x?e{Ebj$J?IV|2nOK>{uR|)9`NP~=FrR}S4?bs48x>gXI#xG>6-#ug@p}hD^3rpFfkeOjx$P(+ptB7v0P>#>ij4!0{8|KW) zt|YU*Vg9`lPi^J=jB)DT)1>@S`xz0LFIZ}lyDQ12Z7q)i7* ze71r-)`7X&(jt86Q5svk4pf=A51WKfheYR*O{oNbA&h2PGRgc7S7%lVFr8e#x%7!r z{7b1n`I{2bFLw$t1 z2B@hvQC%Az>06PgFQO|0GEhjpDO0Ja%g{M0uUtYK578)8KAkS-f=aVQwBs$j8%?c% zZn|A1)-IWh@CLw8MABGGP}ZgkYYbu#u(%~s03Yw9cz~A^(j*kZMPh@;BLOuCmLcf+ zHdIUX0WrfRt$$z!dU6D500{UY#7&|G1sU=K9L-u)Hy2PUj z)@zt?DrzDeu#u1g0YXiL>dob#-K#>Xvmf!YVf)SSWAd`iAr=bZ)r%+rq50D7^1%Vk zL@!~yj>ANtzBYkJgx14x1qdJztwy*!#8Lk7!2n5+W!3>9SS$oY$Tm?vD*gaUDht^L zoQ3(TMzI*Y6+6J<$&zZQO|Y~rE6p$h!zYqgk%FI?@0fbEn4k+ik&og9qObrcK=qO~ z$P~3hmzq6*0_6qOiICHj4aBg(mNjr(7>v%DM96WaCY1wg5eT#}l@F^F8X0OyH}dsQ z%*FqO`r!ezU_=D~mbASaFPC1SE2ZzdDP5(DA2Y;;z>`Ot$ml9GU)Mr6uQV)ixJpKL zV&1HW+N7)#>mj++MXHULoe6pcb@Gv1H0wbI=a5-X)ubPPsKnb+VX0XIAmmbwEr|rW zMPM>jlpVZ2@pz;R?ySnND1x-ABptP>3&~jC*^ug35HbLiW<}seSCwo-g&$y8W1ujp z8XgrCA;`P;G>b@(mr(u*ormQFDL(K43)3&<@0t`{Lg|FvGl&*6VcBbF5(ucgkcLt( zIAo>@o57l+MYgCgGjf4FE|e6(%(Nm6R=TWa{a`S+k z!a9W8MG08`wWqud$^j(d0$L=Rx=DZ=b99_(F<&KjJK#?Cr(r-cxIUWB-=GbLQ)ttL ztT=tkKlRZ(7?QB8iql`oPY!sHOG+N-st&3G>tfedz~^>`xl9LD2beMB(2Fs6+?Gd9 za>T8T5i+p~eFYRBssn2jJCEGzh+DEKk}Pq=%@b%Ic(j@Kv7j08L%Twm{?t;$K+Z7L zw5wd%GEON_fdfQ_C2T}=BjLqbx;b@L?SdxYz8Ri6HS5AtHuZJ~{5IeDq`MRD8V(7S z5|arzL=Y#tR*VDL|tF6@qKWnmmKhtQ$xXb{8Trho%?A>gKm zO5}bgJjfFj!TkWA@(0=a3fYp9MQmS<^wa8gVNpeE72P~KlXR1rQiiRhWgpzeKXEhK zhVSWsZgeo0pi`NE8V)cJiZXH9Lq7lnW>pkIIYDEoH(P|zP(aykCYSWV*Rj@YBKgj^ zIV)om>FSL4@NWiH2KSa@Sw0_yuJDXq@Byr<&EzN-+?l^s-Gwa#M#QkG z{qIPb3vMfUw-M}51<^|Vqn(g|vES+WZ*o zDX4Bza|KG|JAl~X0_CbuegAalPGfe3xI6Jst9)9Sxu!EJHKK9CxJu3t>86u-Pg|7l z+UdfKw1vqeq-w!XB*2i`OnYRWRpI=7(-+*EwY8aaW8y}vqGmFb30*VKB_$A>bARwv zm%v;xl=iW)QFKZ*2d?CbD4%s?Efep};{G5znD|MS!*}v13%4}&M2x_Fw?LNV2T4Up z6C45=;B)#;zGC715uT+6ZqkL<`WGH*hQvV_`o>&2O78-jxFK_5rR-TfymS}(4!z-+@Q)B4=%dzWGNfBVFiCDU$XHq z)<8MwrioifP~WAC`3x}|q@_);z~2oJm`EH#n}AX5+BU4SwAT~#W&02_LD0Zsw{mUj(?6RcsAdm&rJr#RiwF1qom$Pi+ao`Qyh7pei)HYL+uM2&7dXp6C;=`T4_JFqs zvTkv-k-pOq2DaCmDmSG^r}+cypnywJD`+P5;1WT?YvK>^YKx}*8=vXdNnJhMn!64`Dp@kWGpnzYzIwR8*&7BP zZr)BTM3G5M7K-?bm!Zf|9obHsk8+;gKS;SAo?|9PEIor2NzKx#-(AtaM~>6S#b#R( z1Z`u~2#09~c zpH4g1ks*e-BWuq+a+V=(%xX3yR~mxOYZ;Lj4Z#Xcx<|e;#3!3QMg(qy#Zhzc@izzE z?vN=)c!=p11ubQ;0c}C-fDCjd5F-YmqLYDmB69d2=pB!@eAPG%mzSA40;6c84-O~ zL)x3-lZ{Zu;}LbbhPG^|S*pO3%s0h*NbI2@B4ihJ!hyp9>0^d_JJW&* zX@EC71xOfBstBnEE*S;?x3I|VX0SNvSCI8)xV`VWyI|TvIYc|P>eu#|?z#Idn(JmT zkF>5_K+OkLcXfr989C4#H`7P5B%=2~mjDP};QUf@iaBo6lcplQNmqa^gvI}$zUwA= zz#O*}d_oEa)j~P6Tw+uLHi!zYOD_h7LSpx_TO+xg2}KiEp67a*aa zw<&FE3uyrQD0%}D7TKcIp9z?LjqJ3*OHwv2hm)VYln6DH^@F7cjVQJM7tDl)(aJdO z7tH(*O!5n6MZ?hEgbMlvv;6~$T`srm35H3(O?^GpHG@($sVVY!Yh0V@)B_&-LDv+a z=ve^+2J7B?z)qlZG)TmRc-`}Rern^-58Zd5juMyB@`hj zS^>)r!nv{yPMZN4mcW4`h-Qu_4+2JtCFB(wSV2F-aV35n!akWxezC!O@-~CDi^~ae zPC&<1f=$}k;uBc;SIFhIxHW6Z74m>BZt2_(qcl3J{0Bs^ylmZoBHPgQC^FejM||&1 zC}*gwXs7rk&G0Ye1IU4D>Mxu^%**=&_q~6a#6}0QWOodf)x4>`=KId zh>^)eRM4<0G;IN*rs4o8E?j~sG!d4XiaVs5$C7>7^ z)MBlKBaO6Em4POZhbo5BR}Y60{U~8*4CQW3-t3L{=AJ-%JTUj{?7YXD?eQLVtN#MP zi3$A9e>oiCjHz;`O0CF2_PC{G`2Pngx<*Q>AnhG+J672da-;)p-S^Q^l}bb0#FGP? zF|kBQ9pwl6LinkXETj(3%mO%Sb1Bnxh#4#OL4#~|!TYd0*OSJsxHVHrdb{G`W<@jx z__W1FQK?MP5)!76ExISHd`xb0#d}y-)81MO#p0*hoj70YHU57Qs)%APKikMu_Ww`r?jZ zT=-#x#SWByW4+-bRT~0;fFChok~4pa)OCtr(Ia}UwY-(lz>lxdI>_;I!Xb_H(eEx7@(kWGZv^nW%U9=|Am^dVNooMy%t!TwD#I(> z2jtTJxc%fmd05ENbp|i{o_c|jMK{pcM0B;f!f%8#fB@%fBBCz)DsSa}$ocB1Ir$e= z^}ErV74;V?`?|g!(kPxp3Lf%P+VmYT)Z9hbrkX5CdP>IE~;{I{u zf|O+fD_bmNUi3^(des$bmk1ZymbS z6Y79%yG_pU!GjEF&@=$$EJYPiHL}77Cs?lM$dLnKzpH(YOdE(#W$q*&55)bL6UiRF zcmfMAA;rG{}A_+e{o|#8B`oWnLa9sRh zKk8aUj`YVvoD>H@pA4a!gn}q9`U(F&9m+op;|=3VeD$fE!y**Sh35HC?}9%b#0on| zvIfC9-X!z3GE+GxIW_163j;yP7qGyizABN+<%%f)8b@VlPB?uSkxD=q{D0*Fh`LugYi#RpW%Al zr!t*IRQlXYAO3q_rsbo0M$kli2po%?c9T}2_yp|-yO1@d;j&P?4|4-455>o_EDw?0 zhvN2zGoTpQ`0bX0H*62&QA6=?Cg|U`FjzFUlNZBq6J{EDKMbFyJ9P)TWav_vF{B_I zAI`i)ZVShq5SQV28P8@r!iZ51ukm)09EJzMxIP*Nt~OVXGP$i z%zNbC2=L~d+^-&>h6tR?TtmK#z^#}wNZpaRmF@_1*{dEaBhCZTZzS%7qbChT!S&>f zkuW;Fq!i4)BX5wZD({|A1Q!l!_w)ec}n>U9WHy?u_bX zjl$C`17H-DI`9)*d60t;wo#ktc85wUoPGV-4@f-$ZtWcm0|`(3U@9|XC^1Cwak3x> z*wc4`p2LU&v(klJMh5(vw@XezB$+C}oy=eD`PCKV<4%Sf4MoU#SD+%Gu9L?F_(JXR zaJ8n?mYp2)h4hNVZS@JL0ZoK72}HD%12>b?BJqAkI?$=O1hA#B$76^x7lD(V$|6ri z;?})&p$hCZ)cDubosbRX7iMM{$c8#h3$QX&WWu4hBNC5gz96Hba9j1bJR=uH;XRny z6w{LF{B2pT9GE2Is&_B-we=%GasL7+B@Dmkar-?Xcv}Z>%Kbia*g}ZuPpjUfZ z1~lh$=nYDjj!lIOt-|V`2LVa?H(~#h2}0an%}yP;LI@feX1O-^&^>*`LLC*!W< z-bCCh)Wwyr%7xGZ;h(PfI>bc~mb<~G#vLBmLi`Yd8>CT;V1I|X?($w^@C8iMZOMGq zUI=R;Z0w3r`e?HJY!ZH#_5Wf={$)IFz+^Jy_(c4{|BG>ND+XgX98E9C_^Mf};YB5+ z*F#tW>D3Unt|rrx@e;Ps58VEuN4iG+`;j6Zv zRR`87zUtL!zG^dsV~}2T64U|GQo!K@?((DX_!(>pyl$I2o=lyE_m^yk^eG5s5E>@J zk2@#uRW=Z^A+Lhq39uy)F96tDh&My%33_O~7xv|Q_^M(Eiy-WP{E2uly1;cQginyZ z3Skq-eKLeA5PVVrFM+SR13?90rJOHO9gy=?K@buX`Kl=pdqAuUaRJ1OAh?(CRay|J zseIKJ2rMANgJ1)}543OfMeqS8@m2Z|yF=i^pl*h^1wtf*n-DGntQf*DU}8To?b+CpUhe{6Mf~1l&bMfXi_>U)4RDud;{G0dUC-zRK($zN#4}LVX%v6_O68 zGKd2q9D}ssW|;C2HbN+Za0|kB2=c)A%Z8D9oO%P9+b!m3w>?=WQi{8K4WhdV`wnXFQlqvS< zi(b82Ydri)i*usmk&;)gO3UEv7VX&d6J}c+<|~dJ%dO20;l{e09n+@yAoN_|)2M)| z3t=PLNvgWv>tk~phUIVfe`I=`6s+B`aR0SKCX(|*Z_M9vXG>CXkLt!kcCz1~c;f** zay1?{SdOTEeUKxsKB(o%)Yr>{M{?=G2f}h5I)xqMif*s3>j(BT{r`D$&o@9I~rnM;>cg+0l=+V*9wvo!JPf(iQPR#fqp z_cvLjKjPu{vW3?rp4(nmFCc=KO@B3iZ{u@&kJ}r3UB|wD<~y$*d(yb*(p1Mg%RYRe z76*;+4_*BL+uf(Mu+u+dWLVuhRZ)%io)_y17mfdBm{t+=fw?xXx%XP26EC{ic?Z0}~{>Ad{&DBfm>@KHP&u%?AZLfA?nqckN=kxoku1`5Rq2gn} z&+nrTPxE|y+j(5S;3r%Bcl8}_lq>(&=4pUI^27<-nXOyQU!Q)H>|tf@5b5)BI%~m; zkkqr=tzSe9dp=&LJRkRX(q8QN9=q81YRVq@cb;|XFfFGB+j3d-qYk|uvfZG^30~-UXdHgrMc5X&Ext^U%*Y=pT%tdm-Rxh zqWWZa2qyCClRZFKoBk5kUw;7WbPpt~{^3Qw6UGL%E;;3*Z@cHgQtk%HH}jOa#};Qk znim+ces)9Ci7Cq#9Ni~4IYR&LzDT3Un>QC|rF$*U&B0cc|8s(w5Y=JdSUXoR`O;a7 zgIgp$@T-&8c1s@YK6~i;CimR#KDS_Uap;Hgt zk~HK^xVVG+(0HrM*VgW%oTLVV&t}EeElV#=WWAmDT7UMrZ|1lnDN*;R{p!{&6TJ)1 z#79KQbecA8yL)VP?>@x$XM;|UQLefs3MlMrc5vUix(Scw#@-*4_;B?IzT`r8e_q1* z!-q981@V)ouXYI?wo@nhzK>G2TA&S%+u`xKLTMN5r6EPD907vx>a*IGJAnzeR}@AF2Z&{Mayf&~Nm`{zC`pW(W0 zkY&wW?RBb_l?Qut^J+I)VP0Z1Xk?6J&Wlm?KRxw|E>_LB-@VxPjMl`V6>%Cb%-7UZ z&mJ*EXSqSOM@iM>QM;mdy>=Wy^eSs!H{UPo{hFGDMt{Bd^@&R9sEeRU;_>!%L$f(pHm2K?h)wj>ESuJrTV>{bMM@c;$qd)qU&&$p z2#{Bo^ziJXL;kmrULhfA)qPtwLTgj=e2=Yr#@3EsI>9CUZFT26!bH9+&jC+iR*4x4ILaX!94un({cIU z;%d>4ZVOT%o32XoRwL;fW@$ajuWteXGyj>Ygk+M|m}cgp~K+LNYW3Athca<#&&$-s1Pse8H`k zvW_jyeb0^Ep?Z`(``&_i7qd&3L{$5o%qeV*TVt4)+NX5D-G8#@lunt|zpvlw$t#M3 zdEM*=e0$6P{vcbg*Yq`dm#>Qw_H7|GX0zG8vl=fh%X^j(fl-G>t!=w;;Ow#JSC!9u z6a^*CQba6Uefir*5Bnh!(YL-2Cc6u>v~@jGbs9$0lohruy880q)9TtwLp_6x7=y4a zE-xOExH_+H?tzxr%JGuBM)H2MOpHy8{%PDga?)$J zagTo8!wCiF|t;Hz2CnnVcju^R|)yuzqc|*p&<$KPhy1f`nunH{Y)yAeE zq^%DsSa`Wr^C^zFk@K5J~hF^X>NsVh|*R%!e zUD~~mW4*WEVY7$jL7TcYZ?wKKI7{i6c4>y$70<)v7kMU5&czMK?*(2;*l^DI8u8p# zy0-a1dvU0-$+VDxNz?a!9x&#^%8BN>+dJ-I52Q$vH=3uoyJ?zRCR@6#LW>zcg(j`iYK-Aj*YH6*n1 zSp)2Sght!8>U{dPGPuw9()Ky={|uX{_pUB#qSyQB2AqM}exJisb?@h~tg|K8!(v0V z^8yu#$820DO0g{oqt3FjL*&)LNJ#(ww~%HbA@!QPt<2}#hq#o!w~}jI#~IWbo()mz zrBsff$enB2`X%N2G_F!kwe$M+W!aAJmqyIKFjdv^vac6G0CT(-OXO;XeGfU||;2bQJZyc#U1Lq|&z#M%>9?m%-t=wv-PE0h=UGv`<<)6ONZbBfNIQ^_ zw#^$IYjEsm+gi7y^>^CG+?w=Y>pv~WdOK~X{vgm_z0AoZ!*NwmuyN}`#hxuC z|D+Yvq+NSnFWtMp;`=iGXrs+O1$yh6HZL>_m?KF}J$5&(n#kKYv%8}A^s}xPAI0Tf z9F-K-zVN|br}x^`Cr*72++b+=!Rv)V(Dsu{HzdA3yyeuyg9-L2iNfyh-M-XrfA;Kh zY_qhnjK$2@oA$19@`my0Q`0Vd3r$~bJ@Uhz$af13-NPlVYx3sgr(V%L#M(Cez{H`I zoTA(*uCdGZvkncESMNeX+Wy}{szySpZ(g?JnO48W%RQfRJ1z8$X5W1mzxtK0;KP{o z72=Y}mavVs%=n#l!=L@z^R@2bb#qsVI%KoOpR32k{b)Ynvd}~jY7TcyM_jGGm|W6} zoFr^{=CkmX_50ahSKaGy+?lyy-kk4qa;Ba+$Mrina;VPxi@i;aZwZ}e=GU&wk9qMp zN%D?Zem^;E@=7agnqzYACq?&R-`%8KUB&eDlGnAX=OrJneEpOCX3Ui6)`VE&eY;jq z<+v}YU3lnLlh(uSV+PF$?_7M%V^XS9#|OW6Q>HK4=h8CRW%{h9UL&5Bc^zqAvwQNV z8;bp>mb@G3TyL|#w${v1=XGRe_z+2?@2~-vuOA^;_S*95n@C7I{#!^LNJw_$0t*Lk zVBcC)`48Sl?~7G|^?|($om_MtFTRoU{Ei-W(+mFS>T%B+1BS`Hj49FK)2Qb9XSXz7 zy&bsu3hzU}=vi+9W8NPv#xrC8U2D7jb+?%#wqFtzw0z-ptC>73WD|4x%X#OzKk4}{ zaY=}={*7@f100%!{?l&GIUCI9;ortenAIBuxva`@i@xq;9dzhxJNd9v`!uZ~oA zaz!`alo^WsZ!2!u+!mM5$~|yij92v%>e@{d+yY;(Z{w2>D?kTVBsKJ7v!W#N8NczJ5W`5P7SWDZcBg(VQ7G zL{Y}CCTyJQ<@WY@R_i_+)7Ry9`8fiYp(-b>vNTEU*^5s3;dT0>-JY=}!Mr8x1WDbb zhVj~?hos!hxc@2h6fd=iIk+G9ZED;E74PHw)@^reXY;R59&UNP_}|6%M0?`K`51g{ z-rTwC{fHOxKNFGWg3B@4S~L>6TOE%lGJhl-~R2LW^1S&rb$~DCE%VTfqfR z{?h1}{>wwsJnBo*L}Rb)b?bRf!di;UtDVe1Ncn#W>90d_jM;cNBpbCnkJvJxZvQ5$ z!|tcY?qA@td7WeJs=a!<{>{5?vNqLaSQ=NjI(A!0(DnzJC2MkBhQ6&hVOnuMc685& z^5RlnK??obko!*SVfId+#x;n9AxaI;kgW z*|huN{Pk2o>yvO3eUIvhFv+$CgXyyt7?0u)xe-}C@|D+-_(grvzFRMGSut&uQGs6Q zsH_F^9#%zPj7X}U+&*sR+5rYvvpRa)?EX(-xwl(@MjiWcx9~~srkITQF~&odU#JhXQUfPbv0ZHLKwi$&_`ySS-9PQW8Spd(zx~w1T4LS3 z{GrZ%>B4azw#Cev;fqh{p8Z}mH)~Yi;XA)JPoCrbpvORW-H~DMzE^!^drY|!)Lm%^iM`o0ls5Vwe}!mj(q`nJ#R2 za@As>+2>l5b4iZE&n3Ns4p+YK_1Ic@({&c>lmCiE9TxIM9hP-{bqfAdP&F<+K~N?g z%lvyfdHF;yADfc0R_|L(zQgA0m6My&WxbCiRD5h1-)oX^{JTpV|5>Qlv8H8XfZzTb zfmQ!faiNJ38mtMTr}ioRz8BrxGB?h@C)p{xOw+D4@cK@fFmKSKzB#6**4?nX zi}%khd3}x3n0WjUv2SZ@bAS894~i?w@9DsL1o_dL8%I$Im$|dUlhqX`N{@yD^enYjf!w`RDqd zJ1DYiYsJ>i-=A&yB=mmI({H=;uHYqS54H8|J~N=cuHU(r>po@1OC?YH&QJ_KG_J5= zs7!id80+x773S_eAy`4MhR_Rw4Fp>Vb`W|)u!rCP!4ZNJggy|QA-F(rh2RFk9fAjh zz7YCB@Pyz6p+AHH5WFGyKo|(YS8nd^*K}QH&tC)c*8%&79}G;KTTEHSyCxBqmmyuzzu>6}kWC}bgY=-s4G<#m96-Zm z8X?I;H2cE+cAxGX?y8PY6PClc(8{V5z-plA8Mau@d?XZ=Ecah>ixW0DOfu>|MQkn) z?9?wzGzwLb-c7)o<^k?~f6-^HCzV1wrbSdITkL6jHhC3Bd}YXNCNxI5S_SL0TJ-I` z%tQ$muY1}DW!zoRbn&wJ*qU|W39R<9v$u(ogXr?e27sa1AS#TeB}uP+v@8?&f)J$a zZU_xNt`S>Tq?(*Ft>wmFUg-&pv3oNa#kl8T0|S>S;b@bW@Kx*n0;7V5%>F!w%>F%x z%>F*N%>F-60ThxLmX;1)ghf7fp^vl%yg20JXo`^$7!>UbN7-#QAP|xA1&|^Oe@f}v ZnW(fXgrb#DDq568Or^z=tVz~rtTAN@kCEm5#-*35_4@UC-RItW&bjBFd+xd0xp&G;O5|li zNYuG<>*}~mRmVB)42Q!(A9&m@=PkL%xw7+$$&NjfkKNm~u+N4yUhRi0BIcu|fo|XC z9JF1-dumZv6m#iLuf> ziH_p$GUb4R8-0%UJ|bqm+KhJ0>Gim;^WL)wUnaQ49awwve$|`elp?XwxnQcKwqJ7V zxrya^2|IdaKWPrV{w+wkd5%#@*|3KazdIW@SH}1T#fd-Y)Xh+R@B6O$(f(~qqR;(& zU^mH=-`9j(r5W((!Y*M+?&{rlMw|Bw+qmgbc!uY3#|qxyp~`X|S19JMuN;5kw4N1L zr}!V5;<1`iTIzFma%G{;3;KS(ZDi*(07b6(5WkUuzxpi)c3kl8mHf(oQS`IE?zd!% zyB@r4cKlE2pFbCSjJ_ItgeuE${LpD%|5vlSW|)4~zH-F5m*vZ-`w4f$Rv3F~_$(hB zX)}1hS%cM{x_$%Zr(|U6pW4|{B1 zdCt{ob6f1b&#MoVpB|UB|Mr9r`bQ4fKWR3)WOu^UG@4!&mSDB~@gIfxE?Sb+9ygtW z_r6}e?TnFVt)VQ%KSpRm(_9xrMJ}#*kvbyVQ>wMm+$`gzC7VQkZ`)riO z^MIn1652N=&(dp*35wL6#OrzZmSI*vv9JC!r(>>nK2>bo^Tpp(Z{fK9?x@_5YsOuT zUKw)7YTDQsWeb!ZUAZJzQ^))b!P&0I?Wvq=#7!g7=L~_7($bRK@I>d@KUDn77ar;r@t5=oqqo7x_;+xDW99v3BH@Gy0>Tj z{4Z@Ma_jpt*JEzyn=PHcys}=^`0k}?oXhnSPP2kZ z?pUM3^L>|e4=7pET-;;LOUc@%SIpY+JEPH;a@oyI3$9IBIOe#ZYE%BgF|WFh+B~bM z{fw9M8oi~9-wtj~GR$$SQ9du`jcXXt@K^u9 ztBM;IPIOX@Q^LBbH)K`EGFSiA-{%xWAn=vt zJ2aiYYnT07ORy}>$m_70yPTk)B2qB_4itb2?$E-`Qg(vBGc@2ZVJpR*V z!-F>e{>6#cZ3gFFnB0wY>XaM&gP9*%(T}VDXQiU=$4yMKrr*>tC11}Zq36fU+=k8Y zYn*N7x#r%~;HA%>>IRIuwCDPIm*_)(X-7(f9y`AwQ zcC5(nQ|;lTEw=VY&bo3APndsj_0msmv))}Cd86;<{as63C+m-XMbO-!zuJd+`#o@9 zeO+1ScswkC^&r3V(e{ zXKoF8f{4C6Ky=zkbQhEi<#5L5K+bt55o_Q$g2Ry)3lut-zEu&VPZ>agvpb1?Jom?_ zVJBg(N7suPHNFF_)ZIl~;`3TaRKAy}mE9VJS%~THnj8+T4-E{KFare~Xw8d4XieBy z7M=1nzO%ep7~@w2FmE+X)h3+DN53`SbUoPkWt75pj1pK6S&W^rQr4*`fM7==Rj z6Twz4cf<@;7@ctt;(}IjL5tCPgG@*tJPaR|p-8F8A<@nK#8BBGOz})Ck%%s6Vq`&s zkyfSX0!z&%{$dFPT9pzgh53n;1_xL|F~Dg401%B9fauW~9ME%gI{u0{wbzS6sTT@g zL1e}_pw37JJHU|iZ8esR!eA%<>g=pR>kkmRo%m2Bix+Q2_t!`12Z%5ppHzN7K)7Q0_!@>>%MC`Ky%3gDoWb zMod+W?}IOT~2E4~7W}Ldp!nn!61(WDo{Kt|5mLF#)w@5N^h}g;E=N89xK3 z((y;$@Dnm#TTV$aXFk=P7Fs3!->ObXors_Lz{Y_JDEgR z8P9_?oZkcojF=dXClI`H|BbL@O9}xY0Yv5B2sO(}V7U+wN;QI>*ySG;CUT%yB4Bs@ zyBHS`TLuFr@ox-UlO?X{03c@ljbLla#=I&4F~%M3`-|vmJg}oiqz}exPQvF2j5JUW zy7d=fX*L)#U|FoXviQ;<`Zx~&E&TWq^g*P3h_LGRhmfU^Gn(-lW2HLKv#~t-9Sj9L zmad`EhX_;NGkY}q5aBwA2lLVRh*+->dJLrG#X?4E#z?I~=p>lzfSw{%rgD|1L=qN4 zZ-$x`zE-7VFseO7SjtqGg!ZJso?oQ^PHo%rmSIbY$XDn7f&5dA3=2_61$2lvlxpWv zMN%D+Wo1y1BxVelovN3-ohQ}CR=5^t4O70Va~E(aIt* zrwD<-(~J96{@qgg2(8Q_%((ezCqyzkATA0$V9)Xgeg_SB2*x6<;EMP zyF*sq%8M7KJ3}g^Dqcfg2E#ctI(^7L35}xDhjvivM5hn>4^oAJCrcU#^B1(Ekk5fe z4p*1{2gLthko3_k0P1qer(RqHtKfu#Sk(PbN2AlnRH4KpV2zS0wD$;M!W&nGE*>E~ z`tE}XU!^Na^%cZ&C1AV=Mgi_}UjaLV;yI_#0D7m{2U#8^ddPf*B^)kbH95SGqJjp# z0!YP4&+vJU66e?$ig8=jGw*K_P6tF0D*OfO=pJCi0=Xh85#WfX5BO9fLA7f(uD4g=<10=p_0R;=7CFqQ?MLJyA;uCgGQ zMsyg(DrhHN4U`j|u20V6SXweyV1DCJWz@$4TC@6DivGHI!&nX#_&$iX0kQ~1;QI-s zpi3^tOI{?FfA^GDr_TI5ku^O$CkyDDy@%H7Z}zPCr7@T4>)Z5A&N)=9r>Sxpkbz+VM!w61c>)Uie)t! zdMO?A2ZIpja5*U81Ys>^t3Tb;QN8dvT62O3w4w1F(94sxK22<91y2VWq(SjgeLpLl_hOKQu0>8K2@6u4DMsOtk*PD;tB@DrJ7b4z74~z zaSB$uR4;J|9*^c2z%5dAkw0sZ-{4YR6y|+9Kq34NSjgXGA%b5p0Zg3F(Y{kepI*3r z1*aEQE-ddH4Qam)oj|uJ7CM8vCA0;4eTs0AS&J@U3;9oVvR%@saPR1n!WK|`Xrp3; zGd5#}Wz1+*QX_yy|A}YPU4waN<3)g-Pv+Ab$LyY@?+1{LV1NS!eSBwh&-FWZKz8QacAWU#ia88j#un zP+>L?Qcoep%jPPCYTM9Dg0aL&r1U!aef@7M`DGh2Y=!?PLEq~`pVLO|jEuSX22`4oFG@}XQ58D2&~*nK;iH-&x-gV#O|FX9Nx2?R%D;1^ z0V*EVa`C`guxoYKC}jMkCXOd;Yy~6b-x(>bGD*2EH7wP{T=2Ab5OcbSeM~B=4yCN; zI^{cEZx4L#m$>RVM<(4(v30VPPS@qdW>nd)$q@@J{w?(S(9a+@J@Y5?OXdxspX1$g zuska3Un(ETPkh0RCtyP%P?`G6TNvq!4phJft@XNo$yL%Cr|T+BRw#L~rWjX;4rCL? zJm=>qFPq2{I)lt0J4ayvkTs_XH^3;bo+b>qorYsne6Lc?;m^^NGsJ*E7XVOY3UhuO z%-&_5lz}?{GB}V+_k@&Fm4+ru!V+W%0N#ZqN&oQ;HVrV#0VD&bOEvwUqo}h)prg8+ z=v0BasG6QFswSU|Ygel`iqe5n^=Apk5PtxL_=m(g#u)Y2Q?sP=oY>j$)hD(wSh93h zn9v%hDk`*|@`ck~Ju4^GO8M4O@B3j$t!ZdYu!Mf}1Vx=A`s?Bndeu~!&3oK9wErB@ zwG)PE1lCgK04O*|7?}MM2voo}Ug6tFd0?GD+t9~zL@z#;59Rxi2FUR|(Sx_89*sLs z^ycM1MhWMM0VHm8#nohkGH_C%gS!p7 z5=CAhy2&D7GxQ$7)C)LE;S4OTkqQZ4!R|(|X$oV9(di!;2AIOZokNw-f2wnM&ZCOx z6v%0P0<0V4wTft|8tbag%~I#EIDi|k&J9uLMtxvV>jlEspb-{B%G-sC6jCs50|l^j zr@cnwlteF`Ng$GF^HC@O$3wJMNm#o(!%&6>(e>?kL`#QK1M%@rWLXQcO6Wx|S?q24 zD~wDKZ!oN&DRojYAY=NJpw~)R6)&l`94TK_Bz*zMfXgp@&m6+pj0S$3_js)j&Bf!J zdI2-b2H1p|(ITJzd>$|;i~oujaX_ETAuf+hfNr7(JHyo3TYaj9 z!?EB$&$r^>U`b3!3DAx2&)Tm<5;FxFTdElYU#OJB3~VM*?nNSy2eDQzF}N4YHW8oY z;ZTtuA0Th(IHAaakID%*v8V*i%q9BpK9rzMxrAx&UZqfj9f+tdVv;HmC_LRMgJh5} zqZwLJ>gl+!54xR8n6d0V5W<0K+%}9AV`cGPMyG?oKhstBg}b;&yhnO(bG8* zH*}d=BBy-N7v57lRjIlv*1Ocf4nN;E@u?tQKpCtDLY20+JjbyPt<57WrZ0q6Dh6UO zn2r`~#Zo1(CB)FwFi2no-j(z3Zp2mm03FRKu~h`(gBG>k9e67O^{Mr?Yyvi3qagvi zD)^>uvBtnempUZ4MEG~Zo(uX4{v)Gd^zK2>a5#hY7 zMig|J@DX4<1ystk%Y=x>%WOm^t`e>|rmqs|W4C}L(2ueJw*5G#@=o8z0<~oWSs>1- z?9z9#Kx3ID3&c5D zAc}eQrOJMVL^zk%Qi_s`2!GxVS9Gh0=p}srLd^ID!(15y5T=N*GCkP9jwMBCt!iS4 zk0qCyXsZ#8 zDk$|d3wOeiQOIB%=^u5$k+}CD99f+nf+OeSjmkHrL?Vx8Ri<2Ug9zaA+{=`CHwl6( zOR8fSN-l)vpNGpaNqyhE`D*^=~cZ~&YTEEZlvque@dg&6ebt1KHfDei zgk1|!el;4BCAygkfhegEe<|2#gf=)asy>Rq1}G*#-|iB21`SZ$iJ=1PRXy>+fuGBw zT<^h>0VM11!8!o(>3f8;0p_A$3ji-*)I*^IsMdSL8V4+|VvHGp) z6tKe8dV_7l(;XPpc$wjeB7}NL?wbg`f!M6O1~;Axt)ySK!d3~`o_+uwAQYgF4~RZ4S%8dI_&)r%EVlH%JUlvRU=2IWq}Kaz z0$B7$+Fnm50Q*v3-JIsCoi9>?%Beq(=Nmi%4v& zDD`RS8wv;4avtULBQ?oc1$>h@O|X;20#jQBkc_5I6A&pRVAWgskV-0Oeu<{m5nV9z zC3Qr=$OE`j;IA|(vm>SIJo`FsIJFhyC>9PIy^bLwQhFMxjTifo*0UtqiY=J}q*V_W zHo$gZJ+U0e+4bN}PZOZ0^@I~IOMvtr5ks(=B##K2PFRN;p$*g)KqNmRMqs0>ctlw7 zz*s&#B3y@6gU<=f{MRWym&3s`TMq7Q&{;3edj^cbP+msDUUvf@Mu;v2HV*Rgw{ygL z44U(ph|}65h4cT{) z%M+p}mMQWHVUHV=`h-aCSqlZ17{KU+xZs^II;n|)$<3nyvh*T2eBpYW8;G7d$Dv*p zn^XV|p3y)oz~*LlK9k#O#n~ zz*nUF80~6+Ygj4?z~c|wq5;OFhA{s<;^>VmcArriCEdOn{YfFAikYMZ(c&np%#lK!rfloFpvQ6P~{#!pzAo& zE+7Q%LV>)2D{tW%=gaeX?C?NT6ER5E`6MhtB@#(U48HWFXTX}o763E+>M7t-QbBwt zwNbxnIRWby>!o_t07+0%NKjhqL~wXQNpNplscQ=0yZZAJx=Pdxy8y%B5c(1n0G2AP zZ$;qk;>2^?)HhvdC`}NZQZW`UtJ_bYu{2?TTNF(bUb2vBpk(&?wh`1#3&4Xp$Dk4R zzP3sJ0?a7mGt^D3!JFb7#tDxeOV|*Ut{DyhT&`3T3kN*|DOMkBiUZw+VHtc#bj*CO z@?m-mWt#v#O4yc5#F4XpPa}~EPqBJ8>{N#@RQ@;)-rJWj$icVJ2JWCdE9HqV@OvN@pMm<*ZQ|Qp&P|ho&yWx*x zY9{X?Rx?rPy#I#Yy(0QpTqkKU)CPk&2q6PH0cD#AoJ0WP}3oT@^ct}D#f z*Q0?d!b!FfVCj}EFxTl=!55ZVv0|VQoFCqlV+M}7wXQc3nk8`MT?wlp>ldMIpu52L zm!1fP3mSOf;U8!Xg=4W>+bb9rbY@^Ypy)g7DrreO!d+bYXOII)UHGYxEvUZ@B? zUQG>ZQ4zj49@b23!12vyVi1ls-Vh!*_I^Y3)2+dKq$>o)w_*$cw(<>3P!OlRAvW>q zYm~Nc35ILH0ZC}xFRXGpu5??zALp!qp?+QN=!z=FHE1&#PXtO@=CK~~r^cne^%VDNrt@!fB_e|Umn3#fs>C3|a zefulE3@ug~8Ml zaE-$H?Y2rN6(>E%n?Ki2QX(}8BfaIVj?$W6eolNzwh>M*bTd>8XSka<-2iD&Ijo%O z3m~9*B*TcZe-K?#6R{2k^<~l^Sa{qt=eEL9YT{HSQf_d@vanxyp)je{6Q(~Yoq-8h zzU&rK2-A}>MH_`zRDe2(EBfhu{CC<2!$IytczZlB{=Q@3k~VkQ5yRU zCLpJ;#3-I^3tIh^SUr#}<-=#`v-s-gDaO~K%(Ks|R#e7W+<22Lo>UJWuXIxQ30&8J z6i(j=Tb}uK6!nep=#8uRS}2Z>Qa6ZY<$>#u4@`W8RA%)L?oQ~@A;TqL`3OUYl>JvdezQwzK#lzAgWrJ4=! z#bbs3Nur?dgrV#P=A(dOz=nPey-b_H)hoRdPLMDNW128}E{hg|=!|JFFT&IkGo zwkGg}Yv=yNbb~?j*uHr&hBqK<-$6AA%L7!xTvGsNx6>k^OZ9CG36Z3F_%=AzNoX@z zZJ?>nxEKRIOPEXXEomboZJ>RihbKotDrPK3Hvk%30Mf>oWj9dIR&e&F-$0{UiC(e= zaA*gqsFIZY<=u0y191)vGlrf9XIreHhq(AODE_JttaXELc}krq#R`(i`Jxmvh(ki) zWm3#XOy6{A0Z{0iSRkj&qM8O!5{*6o97P7zs2OJOxj&W|_1iXM9t*|!mHlb)-U22HTQ z4$Bpo!wTR4#d9}7CI>E>m{(4~fi{5Rl6mmAQdcsqU&-Skf2T58_0wW#!S^KT-DA8?8GLj52F zxRm;4b9mguznR_WVDeRzk_hGmEl9^2(X54P^YAPltI0miVWerXu$&A45bTCj;kmDH z8(@ZnOI3Iq*sc2l5H`W=hJ7{I0=r=s#B7+O z-T*LCBSxyvNU;wHzX!P7ht&l;O-8=D^O`LX`6>q|(&JlE1fMh$ECXdy29QhOlh!MrxEhwOCrP^gFLd~5!_sXb$)%W1OF;I}+JfulZ=ckLoCRcmyGbB8 z{)2t|D%fbj(8mzE!XGQT0XoMiU%sk=iJzjtD*97E_T+78ML7b}U*>Qb21Ox|AF5=40m;RO!mEl9^Ik*^YSqdSTHlpp7h{$MK2cKpu;r1AhTFcK|YPupKF(S9bs= z!R0VpWL^hgooxqT5&&ST#CoJBSYuNX(}J#e?pcNIMZ!0}nRL zfn9|*Y0f*d6*X&<4!rBR$h;Fd+MrJTRzqbL930EU2(by4(P(uiGL08SBQqfx#;a;V z@j^0+cc=;76_UmJAqVi%-T=Zw8}OAC{WJ}o&;c8_IiS3+L(bsxiuNH75$VXg--s57 z$N@Oc6p>qc+OA}eD2j2~f@mtl5_`Zaf57cz3`W2!A=T-GDh z)`c8v=+HsW#EYrbwqfD1+z>kQG@7DM_7&RR0xcEs;N6M!Ewn?Q>@FOQAtgM8`+5Ic zs7Rlb*d{~2!u5d7G8`G*T7@I=mqZ-tt0SxDFkQj*Kyn)zWIA|HgqMe zP35>m^KS-!H~QWKRMC~RF&R^@wpLi(rPE;d5De=I?uy4INNfbQ-fn{Yj9^OBsYmmT z$eE_QF~d7BeW^vx{w>k@TJ+Y4^fmFRP&4#x`0B&=jPHymn)+A{-LC=#8k2psj6iRl z^iu@4P@*wu!85vr(v8U;_5uLo_ZMQ$t3nVLci2PByVLKg{+9jBee}VYw6b%63kMi! ziqQODef$v@IsHHM@znds+l1`P(|M?zZ$d8M2J?TyAcT|853qIE8^C(T0myogqEr*( zR^jf`6hI1Zy5Il=DNM`ye$paZ*;uldjo?HeNf#prGcuUB>n4($ks)qIK#y%j1TZZxU>Nd6kAU*q}yBKPx|ENKF=A;v^_68bmPPz;jSILggbTJeN zXC&6I3}8@zdVT?HU-1DD78c7e1BYX#=pH&{PR^9!j8h#w`5rs5;cDrfaAOS$d$kNk zkLbcZ`7>c+D?i>oBqS*2In+)09-zbFwH($dBuijF0Tp$yH3U9@P_@%10nXOWNZ4JW zlofv1ZK&H=T^fjvcLOSasm0$6?%ibtnS|*+R-x0~NPk;aIq)jTw=n}NB-OXJ)VCHS z@SOPDVp(z$*pO~3-Fyr|Ns1|y|uWbg~`iC{Fn6F^gWQ}M!uI|m94%vU{ zO@0%c2#f^3B&Q0$j^K$!$+w|awpCPUCNvBv!DR-$`p$yvX~D9#Q?sVt)8_wCWk3yb zvLw3+_hNH|VtxWBHqnwa?$^<0Rx0aX6+;g?s!aZob5;r29NKS5TFJI!X&+Rw(q37M zyX?9V+l~y(O1=Nzm(HTWXxu_VVpz3F1k6IX49D7}E7XTfyj_>#ir`HKfZttt(;icu z0{AAi6Wgiomk=R7YDLJ~9EVF)0EL6}waj1hxExj%&{7800KQcJs-8@5gP<|5LB}x- zIQ93~G_OQ!yOX&x^9`_jR_0TF`icRtCgDD%oqoZLSQu-PU*J^A(wY4Y)A~hc$-?j+ zh_3iWXZ1Ji=`WbILIY$h^@IoBFu3p=G3C`Bq?p(Fz^z}p!ZsSKNt-F3T9MjZUV1Wm z(~ImnD;`Fd3NH^Lr5_=oCndA=rG740IOyrSc!a`SL;)SW@Fr}n*+CErzE%Z?@^k<7 zb)(cp_-vE_zVhFc=CaHH04922EeVf zcurNnQXk>aEie@pa@w({JwKSz!IU8}7>XCxIoZgm4{5^llB2*run6zl2g`C4#!T9c zR`nq*L<%@*Mx^@KPQXWm)!OJ-A95;h)J>#oO?Ky5-$YK26BxKuM9AZjklAl1y4z*K53iIA$mFj6xalJV`R*@he} zY~I5fC=Xnr2s!j64fQVFV3n`J&+8{&RTtCtfX)uIkr}bDjLRVPH^pHIR7vJmKQ2;~u z*j=6v$7CIh20vVfztQzhycxWo;($m0db}az@ZgZpJLaQY2E5UjnA(895_%RC2T|#X z^U()}=*Am1f4d%OCT6P>v3oaHb6~XHgEZ>D4NAc6Lwq-wnskGLVH;saujs_CI261Z z#TPGCz124`*!%aHtG9g}X|E44J}HC{ z4boQpLEY$G6hVdYY0DDyOoOxkr{m6d2S6k2#FjejWbh95h(sbSywKDRh2Eno17}YLlFCX9et-XgFY)NDD zsz2EQt0AGcY*ve3=TzfUZ#ta`-VG{Z)!G>|vJkU5-lZVOwmv9KE+E2ME7`c;hF; z!gO(dBXV#coqCA&gM z7E%HhRu3smORtiG6A!;z%m5x>nwVow4G%4HBwup1mE)brZ(RLB)8JJt-nA-LHGcXF z4D#Z#bTnfC>0hWZ+Ep>GyXYhCRN{5T)FM z868L!p$#lG6M=#RhY#^cDVA)Q0*Rv~3XC-H3&DpHrzX&#i}o zTq*W5N@yOc7)18gVX@9oCaJK#X2)RCrwa?21CZ1;m>!oxVS`DE_dFLp9SldftGP(i zg`CYDhZeYy?%b~ExC=Rfcj+P$xRR^59%!vA9GiF6qsOkKgXr!tdc7&8tD304>LN5?%#8(iwX2kFaQi_C|TgZtq^@gnTR#ZRYcJ6uMK?SepjUQzoj zS@l>U85A&K3m8(q!ct!WF7j*HEj)xOhmyUyyV19yrwARzd*6gu^F=(8s|nP3ev4Mlj7F*?s+0*ab|pFrsV_X;nv zpW)67DCNVRQ+)WUnRwgjQB#L>y-8!8l|YWE$FtUXzT!a-sJ{5rNCDSY6B|0Erq^&zKfZ-O2Fcs0D+aIznF z1PT~VPUck~MMsB|ee`>%4+Os#358BWo$|$SawHe*(7_LugkTipM;dYapjbb0j&3*D zpGHjRFdaSg+>ac^jYke6$i7(G5oCd=HVspRsXP5<8tUs$`ao~b_b1JG9}c07{^S_0 zF{<+?r|9Oe+rAEF$I!@;WS|+V!3aQrY88{MRKtEZ@Z)ZDW+Z9HJ&hVhl9g&z4@DOO z$kE*8NMjV~%vB(lQQ+mAb(rln9WA!k)cxgy(npc{I&k@cJCg0f9vEb)%LS9`=tcv& z4&5F__9gLqg&eE89iw5~Sk)oN(PV!d&lpWch_6CF;qd~$Y4)_1&`zj)G-=G;fnJU# zZ84F4Ai1d97w8O?4*W7&>0=!N`!06hC>|j%vUT%ltV5ZBu&M!VX&~9nXova)C|@b$ z#EbE>4%XaTcA%euWM8vAaCTFRhwm6?u-6Qi?4cPFIuZGdAy;Z!!j+jyTXDv$1(l5< ztvU~d65}J_5H5*fmHBNuY9B)mFen5eA|}8*2|gH@9TQ+rOAkVRW6AEli@+{nZ>px3 zsfAUH5N9REcTtS6p5tk$Uz$kQpwzKsI2WN8V@WHu`nMsG1R6gC*+|IVZgrp;Y#Z!I zcJAQ9{yG}ooxqiyn!VjNyj8*e<_KF@=-I{^qYV<$e&8Zt0BvP;0F9#i0VyjMo*KGA z29WA^0QeR-g#7`X1ig`vebnMm$Rr31u^aLYBJItUpjXzR!~URFG1*qWQUJSZu%{4u z584t0mb(ts2a%>?wVM9G(#MKJXelCtNptQ#)GL_m$z6d)29q{i30fQs4xyj>QPL=~ z3wjt#3VHP%nKqos>&TdekUhA`Xjlm8t$!Tm7f!rGT5AMjfHz$1CHeLc(vA0VAF2u= zy|`15{y4I`yB`?*uO`7u&v=2Gc{b1|sHLr+%r>2sJa-zKh8?sPy9ZzBh2qAMHTnTl z=UX}ru+)pcZZk-^JY3ZOkWWr zbsFd}eZh>G4)HUm&Wm2KXx2=J`7>rk&73iN@q+nwOJ*;exyWIfboSgCQKM(imo9U# zw?|JS$mwv)FxC;6LeO&(Gg~_1HxNIBkOi>A{l&}+h<`u`f;5gX?4My*f8~bp6Un>0|FgvQ%E6P#E?nj4D6;nd-hO-Qh0~+v&01tXox?F% zD`p0+2YZFk0!|q%$bW@2pAs{jDfGuwGM8TkTj;HN^OaYokrE!Sr5cH6l8pkb31TL0 zGHk-1mTGoUilWI-zHCfi_5KA0DfW7?fBzq0GPc71sp2yy#Z3N*zlA$jAZBhucyLzC zv_Z&+upL5Fj+oKDs16Ix!!A<^FPI^iU4Zu+=fDObeh>LoK>H~hOy!h1xSqkhw{{sa zPI6VuEPxP_FJ|&`VIj{GGu;UiDD)K(smFGCc!)pc^>Gi3i_fy#5eyG%b0vXe1XtC5@Jxi zA*d`H;#H8Zfj9)>l@MQpcprqlpvol>R{SYu)C2gAs)N5H7|khb|!fxcJ{-;J1ey9@@H)w9tAC zEnq>)4;caxUpb|-#(^aD_b{Z54{a0Z>)iPLZ-Jm-$A9t465xNJbYhXv_G|gwwKpnf z-kCh6hoy@|*tq(^>pHV>F2oI|g)TMB8oRNf&RY!TTHkYe(uLe}aFkZ6zD-d$Z_K4j z^Ku@X3R)#d`Ftkm(Ukl?*^_&uP5TzN>EXDQNqIL0d|Eqa=Xo#x?q^rr*mz}*#Ng=F zS-w6tXT2=8##PVQtKU}U$Q6OOA&-Ot_hIb?ZY%H%a<&T}ij?J?6w{ zCnPPp)hC1~>K8cifn@i)-NOT~*DtXdV%YO@#IkW-UOSH@%q+V;d(KuHPma^;yIXfhwYKc z*S0Jj;W%WA!4Jg~52>g58Jm@R6JNY5j@lpIR?l0!JiK%1>%R(1y?nf)IiZ1YHOs2x16& z5IRHX0zn^w0R%&3`N^)J*^2)`v)Nrivlb6_#m$|(d)*vi)R*=@n^twAwEu|US6AD4 zOz@r*Wft~8E}1svsg-RHyM8gz)$bSY-fzLpTws4>$r=7f@BXc~*ALw_tjD5hH+sZe zbG2a}%*va&@=^Bf=vxOC?=2aB=0kFF#?~v&ojm-pcAUkr z#Hu>$XCDMxd!~t$mv!2s7~;FGXK=!)`<}x&3ob4*oaXk_x8mK!jfLjHA5TaGJ5Kfv z`uTA0o$Qq(>o)1TJDPH)kL30&(wno}W2W+x`~9fLya!9ym7nPf!AMzt#@Lywee1WT zI{weuIb%@M!Ba!;y`R;#$Lqn5H(?VUJHKC0SiNMuMu5TT@&)ZyX=WjXYnPj^nc4Y= zecguBqpIUBcJ^-ia>`?!_|?0OC;IFfr1E*4vTih-tIvDt+*0%_eJk(!Ohco2|wU(R|+O9@`>|?mOFP*vbpyDc$rb zi>tv!JuDltPN$w-v0P(IZxt-C=MxT-wE!bY>fX$`(dd2Q~zDV;|? z_?EENr-~CW`oP2$JpUCsb7B@O(p%*9(*_y2pX#2ytK>_+_+6n^I<*cr*L_+PHNmm_ z;+{huk2gLvNV*?w(BOyi*6JzCFJSfD{7@ay|M_m}Uy9T_mb zc$pyP(6Q#UuthFn<=UwaMwCxKc=P7dgi9y;bjjZ!J{tGq_3X@fpH5EXUFl<3TxR_j z8s-?i|D9;-PwiVpqP|mejZ1&wJ4|%0iahd#ubg^S;*PtJP)qL%8F%=5Tgp z`>z_fe`7=7lyBM<3AtwmOkXE5etAKXw~#P7;(fz8203rtK6bBxMZYUApG0d7IHGy4 z#J8g9tZtK6y^*m_=760GZUx32{X8~4N$Ebr=h3`BzV+TTVma>!{M*J{W9NR_cmA!O zmsmX?<~=Nbq72|S_KJ%%{nq_}hyUnr7R|9kAFpVd;QHj!=V9CY67D{~U9$D9&+eVj z*Lu45Z5LLgT@22tw|g49}a`WkfLPjXw z^}TwtmKQWWN^`-1Ewj7c@q2SFEjen_%!K2`lRl^H4c{8`?Qqia%Ud61%?fS*bEE0) zZG(@V%a84QyL0iTPf=Jw#*WeYf0O84AaB?j6aPI>9M;6xo5H4= z4xxuu3_dk~$cnY&Rfz0y1NNjR3@B-G$v z7<**mfKkWq8r|ys>htCmr%m^t>M`5@=#w=!>_(4SJjBDo8*SqHJmXmF>Ruh=uF8>=zDVKPoqxEo&5FjJl9n^p5W!N zJQkg-b!}hF>=WBsgA6YYX-W+5DVnC2TGr~{->aRPjyZqy`})AUrUzGU-)<#((r@gD-WfU$O>?>sec}vylHCUgqArMJ zdyX%Mhe7L_PsJWec1^NVovFF}yj{O~_v6J|#!Oi~VEk_P!L;k9Nqzd?xb`hELr&DU zCjJqAgO|EXSw0-A=kCANvkMOQuWu!{g1{6-y8Mvn(w)c64x=?CA*LQ z(cl#jpX>g7US7Av3cZ~CVK>jKOt$yWvOTvbA}g|QSItjP3L=IV_>~kaB`fCic`ch_ zFzxis$J>qj9V&RfRfoIu=N~Ctt01l0o8o@t8%Sc`hK+sxz|nDkpK0Rb;TNpj7fs%t z_|mxUq2OJGLD=Sr_q5Iy?l7E_|D!o3@F^PbWb>9V@0!7rGUm504w%y`BQI|GaekhQ z<#@NB_LQspl;mRrx|5@aSFG6HS=(Uj#QqksJZiMEd=Xa9y??7`A6Czt+_q2F0p6{f zdz~2}xOAmnU%V{uQsaQ4F%|`b)6i~5@073Jw=0v5h=|!+m$q-*cxFlXWxH|K!oDB= zykPF->h)f_S+h%H@Z*$lo!a}~ zHROLZFWVj(7wvy?@1q5eJ$^JFlx^&NVfQWbn3S!by!<D0fZ9YAu zdBWDR2k*{I8-4Nc$?XPR+%^ssO&Pg#zr8WDb4ibnV{gg}M#|5B+kfWSEFVNQau$2W zC0Q7J@eKSX8`yL78%u}Jsa>~6ozEJO7O{hxNHjJ@_HAjqoe^!Y`!T=invthXiU+TE ze`Warte*S-R!=!rPm}*V;d%0V-JjmKshptoeV@2}J9R5Fzi-H#f}X3!)ZIAviSqN_ zemuc#&+S!>UrUv5^#hX4cliW4mmK6YjqJTAIO}}1>#MX~o8}!H^H=qpdY|!njzdo=eT~k(QSIs&gnn2y*US#POliKmE3)ZC~Gd4nW9q8LA!Ph zi`bajr-OSW2!oG|% zcaz(0)NkuW&)zKj95wf|y;odDUU7G=M{CT7#x?t-t#5kv)Fxx(@|f3y=bek#w0Oj` z?CeOh;Ze8F3E$*ibxbCvy&FID;*KAdwxM6W207|oSU5trSaHK}r~TS+-oRzb@*h|| zb^lh6wJE44WspPg9O0CY(r%x!b|lP7?zefHuJw|j+WEKc>08`5YWewrzv+ukC-x2r zeIp-kG;*$N*a1UdD>I)VnqG9XhG^=J9n9iMCE_7d*H~)1l`mb^aH zmY!{SGvmAd^2lProM2~qVW7Wj?7kfRekuy4UT-#>61ZI z%bPx0j(9`V?YJe-uCKJanSFvGFB}}49U!WQjoZd!!tm5*1ziN!+^SV;-dhKr@u@gz zYLi)U!(4mihuPC*-R>7Z(Rj3>xSY3SN#)&cGKb0BxR=-e^3OACW$~YC*%vw!^&vKt^wdQhc4XVG> zQ9=2c-~^3G*56Uf0^A5NbgwaVn+JpDe=1Dw`4 z96H;ne-@R{^I~Z(J#K@UDosm&R91mraov>bXH8z9w0<=`FO5Fd{XmmV(TYzJJKvie zd7Lbn|GmUf)^oD|tArF2)xCM*uGbm_^Twy|y1cF0%&dRisGy}Iw`3%2Q>1uxZWD~% zSGM=!c)k3G@_g?*o0eRu{t~cg{jq*Im-o7Q{#d^MeZ9xsDf$w^cWI#YtzdG?%#9G4j)NvYCo3qaOH~Lp0Sfkla?*q zuxGkNoVM)KLEEI3<406e8_$opus4j0RMUsl%{tw3x?Y239u+f2mBrkn{YD2o`eUB* z!rEaKpN<&Mer&TVFLi}!?yXg!QRSz~-}c@nleMW9hMvvz&E!_fLaz;KE!ZrG5*s&7 z(qCnGuiHMAVu9bdLl&N8^L9?Pef>zo~$?Fne7>tGP0vwOdVpohSdTrG<7Dpr!4RYn#k_b#FBLV``FR znkG|#hQ^i7@j86lsQV>JN$D2HKf>qjaqKm8;)H}g8lzKD*G*x6)h};ul*Es4d%3gf z`0%i<_vjas{QnOj-&TnW53GDymv3>L54b4$|CT*}3j(BrOn)SGk zlbw|6`zw*_1(w-e7As*%w)^`n$s~=+LcIHcw7-J%Gmwv~T>!1{ft*U$WRFIGtUhB_txH zl^tcUHDI3RYDw8LwL+HT7iE8LX5l~e+<3QS*xuT;2Mz=WE-iC@7Sqp@Q+#Ig^PVjU z*EEy#m#=L6^*%(%PVFY9Y3=j-xZ&Nn(;mU#60v<&l*$HxDfMPBx@@9B1ZsTF+4 zaIT~B|FcKLBtqUEd+>JJ8{ou_>Z9v+^Uf)HzB=}DN{OA$gcj?|4BO_!+9l^OMN74x z`DX*fwm=N*2>!DVU=;cNe9}{y!wgDxWefXro=4<9*zjofL_rB*k>nS?%-eI99cgRc k^vG~|Wq5OwZ%z?WP)`f37wzrvLx| delta 24472 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 value substitution in ota_update.cmd -for elem in $tab_size ; do IFS=':' read -a fld_elem <<< "$elem"; sed -i "s/${fld_elem[0]}/${fld_elem[1]}/g" $top_repo_dir/build/toFlash/ota_update.cmd; done; - -# Look for mkimage tool path -uboot_default_path=$top_repo_dir/u-boot/tools/ -uboot_ext_src_path=$top_repo_dir/build/tmp/work/edison-poky-linux/u-boot -if [ -d $uboot_default_path ]; then - mkimage_tool_path=$(find $uboot_default_path -name mkimage) -else - mkimage_tool_path=$(find $uboot_ext_src_path -name mkimage) -fi +for elem in $tab_size ; do IFS=':' read -a fld_elem <<< "$elem"; sed -i "s/${fld_elem[0]}/${fld_elem[1]}/g" $build_dir/toFlash/ota_update.cmd; done; # Convert OTA script to u-boot script -$mkimage_tool_path -a 0x10000 -T script -C none -n 'Edison Updater script' -d $top_repo_dir/build/toFlash/ota_update.cmd $top_repo_dir/build/toFlash/ota_update.scr +$mkimage_tool_path -a 0x10000 -T script -C none -n 'Edison Updater script' -d $build_dir/toFlash/ota_update.cmd $build_dir/toFlash/ota_update.scr # Supress Preprocessed OTA script -rm $top_repo_dir/build/toFlash/ota_update.cmd +rm $build_dir/toFlash/ota_update.cmd # Generates a formatted list of all packages included in the image -awk '{print $1 " " $3}' $top_repo_dir/build/tmp/deploy/images/edison/edison-image-edison.manifest > $top_repo_dir/build/toFlash/package-list.txt +awk '{print $1 " " $3}' $build_dir/tmp/deploy/images/edison/edison-image-edison.manifest > $build_dir/toFlash/package-list.txt echo "**** Done ***" -echo "Files ready to flash in $top_repo_dir/build/toFlash/" +echo "Files ready to flash in $build_dir/toFlash/" echo "Run the flashall script there to start flashing." echo "*************" diff --git a/device-software/utils/handle_bash_func.patch b/device-software/utils/handle_bash_func.patch new file mode 100755 index 0000000..f4411bc --- /dev/null +++ b/device-software/utils/handle_bash_func.patch @@ -0,0 +1,41 @@ +commit becb32bb30a9fc8ffe3bf59bbe346297a5a8899a +Author: Richard Purdie +Date: Mon Dec 8 16:37:26 2014 +0000 + + bitbake: data: Handle BASH_FUNC shellshock implication + + The shellshock patches changed the way bash functions are exported. + Unfortunately different distros used slightly different formats, + Fedora went with BASH_FUNC_XXX()=() { echo foo; } and Ubuntu went with + BASH_FUNC_foo%%=() { echo foo; }. + + The former causes errors in dealing with out output from emit_env, + the functions are not exported in either case any more. + + This patch handles things so the functions work as expected in either + case. + + [YOCTO #6880] + + (Bitbake rev: 4d4baf20487271aa83bd9f1a778e4ea9af6f6681) + + Signed-off-by: Richard Purdie + +diff --git a/bitbake/lib/bb/data.py b/bitbake/lib/bb/data.py +index 91b1eb1..eb628c7 100644 +--- a/bitbake/lib/bb/data.py ++++ b/bitbake/lib/bb/data.py +@@ -219,6 +219,13 @@ def emit_var(var, o=sys.__stdout__, d = init(), all=False): + + val = str(val) + ++ if varExpanded.startswith("BASH_FUNC_"): ++ varExpanded = varExpanded[10:-2] ++ val = val[3:] # Strip off "() " ++ o.write("%s() %s\n" % (varExpanded, val)) ++ o.write("export -f %s\n" % (varExpanded)) ++ return 1 ++ + if func: + # NOTE: should probably check for unbalanced {} within the var + o.write("%s() {\n%s\n}\n" % (varExpanded, val)) diff --git a/device-software/utils/invalidate_sstate.sh b/device-software/utils/invalidate_sstate.sh index deff37a..8947c48 100755 --- a/device-software/utils/invalidate_sstate.sh +++ b/device-software/utils/invalidate_sstate.sh @@ -7,19 +7,14 @@ top_repo_dir=$(dirname $(dirname $(dirname $(readlink -f $0)))) -cd $top_repo_dir/build/ +if [ $# -eq 0 ]; then + cd $top_repo_dir/build +else + cd $1 +fi + + bitbake -c cleansstate virtual/kernel bitbake -c cleansstate u-boot bitbake -c cleansstate bcm43340-mod -bitbake -c cleansstate nodejs -bitbake -c cleansstate nodejs-native -bitbake -c cleansstate mdns -bitbake -c cleansstate paho-mqtt -bitbake -c cleansstate rsmb -bitbake -c cleansstate zeromq -bitbake -c cleansstate iotkit-comm-c -bitbake -c cleansstate iotkit-comm-js -bitbake -c cleansstate iotkit-agent bitbake -c cleansstate oobe -bitbake -c cleansstate mraa -bitbake -c cleansstate upm diff --git a/device-software/utils/sdk-populate-clean-broken-links.patch b/device-software/utils/sdk-populate-clean-broken-links.patch index f861822..a0a8561 100644 --- a/device-software/utils/sdk-populate-clean-broken-links.patch +++ b/device-software/utils/sdk-populate-clean-broken-links.patch @@ -1,5 +1,5 @@ diff --git a/meta/lib/oe/sdk.py b/meta/lib/oe/sdk.py -index 4a03e96..45167cc 100644 +index ca349c4..576cae8 100644 --- a/meta/lib/oe/sdk.py +++ b/meta/lib/oe/sdk.py @@ -49,6 +49,24 @@ class Sdk(object): @@ -25,5 +25,5 @@ index 4a03e96..45167cc 100644 + pass + # Link the ld.so.cache file into the hosts filesystem - sysconfdir = os.path.join(self.sdk_output, self.sdk_native_path, self.sysconfdir) - bb.utils.mkdirhier(sysconfdir) + link_name = os.path.join(self.sdk_output, self.sdk_native_path, + self.sysconfdir, "ld.so.cache") diff --git a/mw/oobe/LICENSE b/mw/oobe/LICENSE index a0e735a..b5494fc 100644 --- a/mw/oobe/LICENSE +++ b/mw/oobe/LICENSE @@ -1,176 +1,20 @@ - -GNU LESSER GENERAL PUBLIC LICENSE - -Version 2.1, February 1999 - -Copyright (C) 1991, 1999 Free Software Foundation, Inc. -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -Everyone is permitted to copy and distribute verbatim copies -of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] -Preamble - -The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. - -This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. - -When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. - -To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. - -For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. - -We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. - -To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author`s reputation will not be affected by problems that might be introduced by others. - -Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. - -Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. - -When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. - -We call this license the "Lesser" General Public License because it does Less to protect the user`s freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. - -For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. - -In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. - -Although the Lesser General Public License is Less protective of the users` freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. - -The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. - -TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - -0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". - -A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. - -The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) - -"Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. - -Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. - -1. You may copy and distribute verbatim copies of the Library`s complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. - -You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. - -2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: - -a) The modified work must itself be a software library. -b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. -c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. -d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. -(For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. - -3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. - -Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. - -This option is useful when you wish to copy part of the code of the Library into a program that is not a library. - -4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. - -If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. - -5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. - -However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. - -When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. - -If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) - -Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. - -6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer`s own use and reverse engineering for debugging such modifications. - -You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: - -a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) -b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user`s computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. -c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. -d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. -e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. -For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. - -It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. - -7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: - -a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. -b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. -8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. - -9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. - -10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients` exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. - -11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. - -This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. - -12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. - -13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. - -14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. - -NO WARRANTY - -15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - -16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -END OF TERMS AND CONDITIONS - -How to Apply These Terms to Your New Libraries - -If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). - -To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - -one line to give the library`s name and an idea of what it does. -Copyright (C) year name of author - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library 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 -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: - -Yoyodyne, Inc., hereby disclaims all copyright interest in -the library `Frob` (a library for tweaking knobs) written -by James Random Hacker. - -signature of Ty Coon, 1 April 1990 -Ty Coon, President of Vice -That`s all there is to it! - +Copyright © 2014 Intel Corporation + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/mw/oobe/package.json b/mw/oobe/package.json new file mode 100644 index 0000000..f8b480e --- /dev/null +++ b/mw/oobe/package.json @@ -0,0 +1,11 @@ +{ + "name": "oobe", + "version": "1.1.0", + "description": "Web-based administration portal for conifiguring and upgrading the Edison", + "main": "src/server.js", + "dependencies": { + "multiparty": "*" + }, + "author": "Intel Corporation", + "license": "LGPL" +} \ No newline at end of file diff --git a/mw/oobe/src/configure_edison b/mw/oobe/src/configure_edison index 2b2bc12..3829390 100755 --- a/mw/oobe/src/configure_edison +++ b/mw/oobe/src/configure_edison @@ -28,6 +28,7 @@ from array import * DESTINATION_PATH = "/tmp/" STATE_DIR = '/var/lib/edison_config_tools' # todo: change in bitbake +CURR_PACKAGE_PATH = "" class text_colors: CYAN = '\033[96m' @@ -173,6 +174,7 @@ def verified(selection): def changeName(newName): _changeHostName(newName) + _changeP2PSSID(newName) _changeAPSSID(newName) def _changeHostName(newName): @@ -185,6 +187,9 @@ def _changeAPSSID(newName): os.popen("sed -i 's/^ssid=.*/ssid=%s/' /etc/hostapd/hostapd.conf" % (newName)) subprocess.call("systemctl restart mdns && sleep 2", shell=True) +def _changeP2PSSID(newName): + os.popen("sed -i 's/^p2p_ssid_postfix=.*/p2p_ssid_postfix=%s/' /etc/wpa_supplicant/p2p_supplicant.conf" % (newName)) + def changePassword(newPass): _changeRootPassword(newPass) if len(newPass) > 0: @@ -277,11 +282,12 @@ def scanForNetworks(): os.popen("systemctl stop hostapd && sleep 2 && systemctl start wpa_supplicant") r = range(10,0,-1) for i in r: - stdout.write("Scanning: %s seconds left\r" % i) + stdout.write("Scanning: %s seconds left \r" % i) stdout.flush() if i == 6: os.popen("wpa_cli scan") time.sleep(1) + data = os.popen("wpa_cli scan_results").read().split("\n") print "\n" @@ -292,7 +298,7 @@ def scanForNetworks(): line = data.pop().split("\t") if (len(line) == 5): ssid = line.pop() - if ssid not in ssid_keys and not ssid == "": + if ssid not in ssid_keys and not ssid == "" and "\\x00" not in ssid: tokens = line.pop().replace("[","").split("]")[0].split("-") if tokens[0] == "WPA" or tokens[0] == "WPA2": if tokens[1] == "EAP": @@ -303,15 +309,28 @@ def scanForNetworks(): network_map[ssid] = "WEP" else: network_map[ssid] = "OPEN" + ssid_keys.append(ssid) + network_file = open(STATE_DIR + "/networks.txt", "w") + network_file.write("{\n") + for i in range(0, len(ssid_keys)): + network_file.write('\t"' + ssid_keys[i] + '": "' + network_map[ssid_keys[i]]) + if i == len(ssid_keys)-1: + network_file.write('"\n') + else: + network_file.write('",\n') + network_file.write("}\n") + network_file.close() + return (ssid_keys, network_map) def selectNetwork(ssid_keys): - i = 1 + i = 2 print "0 :\tRescan for networks" - print "1 :\tManually input a hidden SSID" + print "1 :\tExit WiFi Setup" + print "2 :\tManually input a hidden SSID" for ssid in ssid_keys: i = i + 1 print i, ":\t", ssid @@ -320,12 +339,12 @@ def selectNetwork(ssid_keys): choice = -1 while 1: try: - if i == 1: - choice = int(raw_input("\nEnter 0 to rescan for networks.\nEnter 1 to input a hidden network SSID: ")) - elif i == 2: - choice = int(raw_input("\nEnter 0 to rescan for networks.\nEnter 1 to input a hidden network SSID.\nEnter 2 to choose %s: " % ssid_keys[0])) + if i == 2: + choice = int(raw_input("\nEnter 0 to rescan for networks.\nEnter 1 to exit.\nEnter 2 to input a hidden network SSID: ")) + elif i == 3: + choice = int(raw_input("\nEnter 0 to rescan for networks.\nEnter 1 to exit.\nEnter 2 to input a hidden network SSID.\nEnter 3 to choose %s: " % ssid_keys[0])) else: - choice = int(raw_input("\nEnter 0 to rescan for networks.\nEnter 1 to input a hidden network SSID.\nEnter a number between 2 to %s to choose one of the listed network SSIDs: " % i)) + choice = int(raw_input("\nEnter 0 to rescan for networks.\nEnter 1 to exit.\nEnter 2 to input a hidden network SSID.\nEnter a number between 3 to %s to choose one of the listed network SSIDs: " % i)) except TypeError: choice = -1 except ValueError: @@ -334,8 +353,10 @@ def selectNetwork(ssid_keys): if choice == 0: break elif choice == 1: + sys.exit(0) + elif choice == 2: break - elif choice > 1 and choice <= i and verified(ssid_keys[choice-2]): + elif choice > 2 and choice <= i and verified(ssid_keys[choice-3]): break return choice @@ -379,7 +400,7 @@ def configureHiddenNetwork(ssid): def configureNetwork(choice,ssid_keys,network_map): - ssid = ssid_keys[choice-2] + ssid = ssid_keys[choice-3] if network_map[ssid] == "OPEN": return wpa_templates.OPEN % (ssid, "") elif network_map[ssid] == "WEP": @@ -488,7 +509,7 @@ def connectNetwork(): choice = selectNetwork(ssid_keys) #choice is validated within selectNetwork. - if choice == 1: + if choice == 2: while 1: ssid = raw_input("Please enter the hidden network SSID: ") if verified(ssid): @@ -497,7 +518,7 @@ def connectNetwork(): break elif choice: network_conf = configureNetwork(choice, ssid_keys,network_map) - ssid = ssid_keys[choice-2] + ssid = ssid_keys[choice-3] break #print network_conf @@ -528,7 +549,7 @@ def getCurrentFirmwareInfo(): f = open('/etc/version','r') ver_str = f.readline() f.close() - return (ver_str.split('_')[0], ver_str.split('_')[2]) + return ("edison-" + ver_str.split('-')[0], ver_str.split('-')[1].splitlines()[0]) def parseFirmwareInfo(firmwareinfo, build_type): fwInfoObj = json.loads(firmwareinfo) @@ -555,7 +576,7 @@ def getLatestFirmwareInfo(): print >> sys.stderr, "Could not determine firmware version information." raise - build_url = "http://ndgfirmwareupdate-env.elasticbeanstalk.com/getLatestFirmwareUpdateURL?product=" + build_type + build_url = "http://software.wearables.intel.com/getLatestFirmwareUpdateURL?product=" + build_type request = urllib2.Request(build_url) request.add_header("Accept", "application/json") filehandle = urllib2.urlopen(request) @@ -563,7 +584,7 @@ def getLatestFirmwareInfo(): return parseFirmwareInfo(response, build_type) def getSpecificFirmwareInfo(build_type, build_number): - build_url = "http://ndgfirmwareupdate-env.elasticbeanstalk.com/getFirmwareUpdateURL?product=" + build_type + "&version=" + build_number + build_url = "http://software.wearables.intel.com/getFirmwareUpdateURL?product=" + build_type + "&version=" + build_number request = urllib2.Request(build_url) request.add_header("Accept", "application/json") filehandle = urllib2.urlopen(request) @@ -589,16 +610,22 @@ def removeFolderContents(folder_path): return 0 def removePackage(): + global CURR_PACKAGE_PATH try: - os.remove(DESTINATION_PATH+"package.zip") + os.remove(CURR_PACKAGE_PATH) except Exception, e: pass + CURR_PACKAGE_PATH = "" def signal_term_handler(signal, handler): removePackage() removeFolderContents("/update") sys.exit(0) +def signal_term_handler_package_only(signal, handler): + removePackage() + sys.exit(0) + def err_downloadPackage(): print "Failed to download the new firmware. Unable to proceed." removePackage() @@ -648,13 +675,13 @@ def genSha1ForFile(file_path): buf = f.read(chunkSize) return sha.hexdigest() -def downloadPackage(packageUrl, sha1FromServer): - ret_value = subprocess.call("curl --connect-timeout 10 "+ "'"+packageUrl+"'" + " >"+DESTINATION_PATH+"package.zip", shell=True) +def downloadPackage(packageUrl, sha1FromServer, packagePath): + ret_value = subprocess.call("curl --connect-timeout 10 "+ "'"+packageUrl+"'" + " >"+packagePath, shell=True) if ret_value != 0: err_downloadPackage() return -1 - shaOfDownloadedPkg = genSha1ForFile(DESTINATION_PATH + "package.zip") + shaOfDownloadedPkg = genSha1ForFile(packagePath) if sha1FromServer != shaOfDownloadedPkg: print "Could not establish integrity of downloaded firmware (hash mismatch). Unable to proceed" @@ -663,23 +690,44 @@ def downloadPackage(packageUrl, sha1FromServer): return 0 -def doOTAFlash(packageUrl, sha1FromServer): +def doOTAFlash(packageUrl, sha1FromServer, packagePath): if len(sha1FromServer) != 40: print "Could not establish integrity of latest firmware (bad hash). Unable to proceed." return -1 try: - signal.signal(signal.SIGTERM, signal_term_handler) + signal.signal(signal.SIGTERM, signal_term_handler_package_only) - try: - ret_value = downloadPackage(packageUrl, sha1FromServer) - if ret_value != 0: - return -1 - except Exception as inst: - err_downloadPackage() - print >> sys.stderr, type(inst) - print >> sys.stderr, inst + try: + ret_value = downloadPackage(packageUrl, sha1FromServer, packagePath) + if ret_value != 0: return -1 + except Exception as inst: + err_downloadPackage() + print >> sys.stderr, type(inst) + print >> sys.stderr, inst + return -1 + + flashFile(packagePath) + except KeyboardInterrupt: + print "" + print "Interrupted by user. Cleaning up." + removePackage() + print "Please reboot the device to return to normal." + except Exception as inst: + print "Exception occurred while upgrading. Cleaning up." + print >> sys.stderr, type(inst) + print >> sys.stderr, inst + removePackage() + print "Please reboot the device to return to normal." + +def flashFile(packagePath): + if not os.path.isfile(packagePath): + print "Error: cannot upgrade. Package file " + packagePath + " does not exist." + return -1 + + try: + signal.signal(signal.SIGTERM, signal_term_handler) try: ret_value = subprocess.call("mkdir -p /update", shell=True) @@ -737,7 +785,7 @@ def doOTAFlash(packageUrl, sha1FromServer): return -1 try: - ret_value = subprocess.call("unzip -o " + DESTINATION_PATH + "package.zip -d /update", shell=True) + ret_value = subprocess.call("unzip -o " + packagePath + " -d /update", shell=True) if ret_value != 0: err_extractPackage() return -1 @@ -785,7 +833,7 @@ def doOTAFlash(packageUrl, sha1FromServer): removeFolderContents("/update") print "Please reboot the device to return to normal." -def upgrade(): +def upgrade(packagePath): curr = None build_type = None @@ -802,15 +850,15 @@ def upgrade(): raise if int(latest) > int(curr): - doOTAFlash(url, sha1) + doOTAFlash(url, sha1, packagePath) else: print "The latest version is already installed." def full(): - reset("Device Name") - setEdisonHostname() reset("Device Password") setEdisonPassword() + reset("Device Name") + setEdisonHostname() if decideToConnect(): connectNetwork() if not os.path.isfile(STATE_DIR + "/one-time-setup.done"): @@ -822,12 +870,17 @@ def getSSID(): return subprocess.check_output("grep -o '^ssid=.*' /etc/hostapd/hostapd.conf | awk -F'=' '{print $2}'", shell=True).strip() def enableOneTimeSetup(): + subprocess.call("systemctl start blink-led && systemctl enable blink-led &> /dev/null", shell=True) + hostname = subprocess.check_output('hostname').strip() try: os.remove(STATE_DIR + "/one-time-setup.done") except Exception as inst: print "Done." + print "Scanning and saving WiFi networks..." + scanForNetworks() + print "Restarting WiFi access point. Please wait..." print "" @@ -843,6 +896,13 @@ def enableOneTimeSetup(): print "and visit", "'" + hostname + ".local' in the browser" def disableOneTimeSetup(): + try: + os.remove(STATE_DIR + "/networks.txt") + except Exception as inst: + pass + + subprocess.call("systemctl stop blink-led && systemctl disable blink-led &> /dev/null", shell=True) + if int(subprocess.check_output("systemctl status wpa_supplicant | grep 'active (running)' | wc -l", shell=True)) != 0: subprocess.call("systemctl disable hostapd &> /dev/null", shell=True) else: @@ -861,6 +921,8 @@ def showNames(): def main(): + global CURR_PACKAGE_PATH + parser = argparse.ArgumentParser(prog='configure_edison') parser.add_argument('--server', help='Starts the server (testing only)', action='store_true', default=False) @@ -881,6 +943,7 @@ def main(): group_non_interactive.add_argument('--disableOneTimeSetup', dest='disableOneTimeSetup', help='Disable one-time setup and WiFi access point', action='store_true', default=False) group_non_interactive.add_argument('--enableOneTimeSetup', dest='enableOneTimeSetup', help='Enable one-time setup and WiFi access point', action='store_true', default=False) group_non_interactive.add_argument('--flash', metavar=('',''), nargs='+', dest='otaflash', help='Downloads and flashes an image') + group_non_interactive.add_argument('--flashFile', metavar=(''), nargs=1, dest='flashfile', help='Flashes the given image (.zip).') group_non_interactive.add_argument('--changePassword', metavar='password', dest='changepassword', const='', help=argparse.SUPPRESS, nargs='?') group_non_interactive.add_argument('--changeName', metavar='name', dest='changename', help=argparse.SUPPRESS, nargs=1) group_non_interactive.add_argument('--changeWiFi', metavar='securityType SSID [Identity | password]', dest='changewifi', help=argparse.SUPPRESS, nargs='+') @@ -928,9 +991,12 @@ def main(): if args.upgrade: try: - upgrade() + CURR_PACKAGE_PATH = DESTINATION_PATH + "package.zip" + upgrade(CURR_PACKAGE_PATH) except Exception as inst: print "Could not upgrade device firmware. Quitting." + print >> sys.stderr, type(inst) + print >> sys.stderr, inst return -1 if args.shownames: @@ -957,14 +1023,20 @@ def main(): print "Could not retrieve required firmware information. Quitting." return -1 + CURR_PACKAGE_PATH = DESTINATION_PATH + "package.zip" + try: - doOTAFlash(url, sha1) + doOTAFlash(url, sha1, CURR_PACKAGE_PATH) except Exception as inst: print >> sys.stderr, type(inst) print >> sys.stderr, inst print "Could not flash device. Quitting." return -1 + if args.flashfile != None: + CURR_PACKAGE_PATH = args.flashfile[0] + flashFile(CURR_PACKAGE_PATH) + if args.changepassword != None: changePassword(args.changepassword) diff --git a/mw/oobe/src/public/exit-upgrade.html b/mw/oobe/src/public/exit-upgrade.html new file mode 100644 index 0000000..29c4ee3 --- /dev/null +++ b/mw/oobe/src/public/exit-upgrade.html @@ -0,0 +1,33 @@ + + + + Leaving Setup + + + + + +

    Leaving Setup

    + +
    +
    +

    The firmware file has been uploaded successfully and the device is about to upgrade. Please allow 5 + mins for the device to finish upgrading. The device will reboot twice before it is ready to use + again. If the upgrade is successful, data in the /home directory should remain unchanged. + +

    Important note: + +

    The current version number of the device is params_version. After the upgrade, please log in via the + serial port and run the command configure_edison --version to verify that the version number has + changed. If that is not the case, the upgrade has failed and you must run the reboot command to + restore the device to its previous state. +

    +
    + + diff --git a/mw/oobe/src/public/exit.html b/mw/oobe/src/public/exit.html index 3eef046..a061627 100644 --- a/mw/oobe/src/public/exit.html +++ b/mw/oobe/src/public/exit.html @@ -1,11 +1,19 @@ - Leaving One-time Setup + Leaving Setup + + -

    Leaving One-time Setup

    +

    Leaving Setup

    diff --git a/mw/oobe/src/public/exiting-without-wifi.html b/mw/oobe/src/public/exiting-without-wifi.html index 001d4f5..7bb2692 100644 --- a/mw/oobe/src/public/exiting-without-wifi.html +++ b/mw/oobe/src/public/exiting-without-wifi.html @@ -1,11 +1,19 @@ - Leaving One-time Setup + Leaving Setup + + -

    Leaving One-time Setup

    +

    Leaving Setup

    diff --git a/mw/oobe/src/public/index.html b/mw/oobe/src/public/index.html index f7afb81..9084a92 100644 --- a/mw/oobe/src/public/index.html +++ b/mw/oobe/src/public/index.html @@ -1,9 +1,62 @@ - Edison One-time Setup + Edison Setup + + + +

    Edison Setup

    + + +
    +
    +
    Upgrade or Replace Device Firmware
    + + + + + +
    + This does not erase the contents of the /home directory. However, it is still + recommended that all important information on the device be backed up to avoid loosing data due + to unforeseen errors. + + + +
    +
    +
    + +
    +
    +
    + +
    \ No newline at end of file diff --git a/mw/oobe/src/server.js b/mw/oobe/src/server.js index 97df791..75e8100 100644 --- a/mw/oobe/src/server.js +++ b/mw/oobe/src/server.js @@ -3,11 +3,12 @@ var fs = require('fs'), qs = require('querystring'), exec = require('child_process').exec, - url = require('url'); + url = require('url'), + multiparty = require('multiparty'); var site = __dirname + '/public'; -var payload, urlobj; -var injectStatusAfter = '

    Edison One-time Setup

    '; +var urlobj; +var injectStatusAfter = ''; var injectPasswordSectionAfter = 'onsubmit="saveFields()">'; var supportedExtensions = { "css" : "text/css", @@ -22,8 +23,9 @@ var supportedExtensions = { "jpeg" : "image/jpeg", "jpg" : "image/jpeg", "png" : "image/png" -} +}; var STATE_DIR = '/var/lib/edison_config_tools'; +var NETWORKS_FILE = STATE_DIR + '/networks.txt'; function getContentType(filename) { var i = filename.lastIndexOf('.'); @@ -38,9 +40,9 @@ function injectStatus(in_text, statusmsg, iserr) { var status = ""; if (statusmsg) { if (iserr) - status = '
    ' + statusmsg + '
    '; + status = '
    ' + statusmsg + '
    '; else - status = '
    ' + statusmsg + '
    '; + status = '
    ' + statusmsg + '
    '; } return in_text.substring(0, injectStatusAt) + status + in_text.substring(injectStatusAt, in_text.length); } @@ -188,9 +190,50 @@ function submitForm(params, res, req) { } function handlePostRequest(req, res) { - var params = qs.parse(payload); if (urlobj.pathname === '/submitForm') { - submitForm(params, res, req); + var payload = ""; + req.on('data', function (data) { + payload += data; + }); + req.on('end', function () { + var params = qs.parse(payload); + submitForm(params, res, req); + }); + } else if (urlobj.pathname === '/submitFirmwareImage') { + var form = new multiparty.Form(); + + form.parse(req, function(err, fields, files) { + console.log(files); + console.log('Upload completed!'); + + if (err) { + res.end(injectStatus(fs.readFileSync(site + '/upgrade.html', {encoding: 'utf8'}), + "File upload failed. Please try again.", true)); + } else { + var exitupgradeStr = fs.readFileSync(site + '/exit-upgrade.html', {encoding: 'utf8'}); + var currversion; + exec('configure_edison --version ', + function (error, stdout, stderr) { + if (error) { + currversion = "unknown"; + } else { + currversion = stdout; + } + exitupgradeStr = exitupgradeStr.replace(/params_version/g, currversion); + res.end(exitupgradeStr); + + exec('configure_edison --flashFile ' + files.imagefile[0].path, + function (error, stdout, stderr) { + if (error) { + console.log("Upgrade error: "); + console.log(stderr); + return; + } + console.log(stdout); + }); + }); + } + }); } else { pageNotFound(res); } @@ -201,16 +244,10 @@ function handlePostRequest(req, res) { function requestHandler(req, res) { urlobj = url.parse(req.url, true); - payload = ""; // POST request. Get payload. if (req.method === 'POST') { - req.on('data', function (data) { - payload += data; - }); - req.on('end', function () { - handlePostRequest(req, res); - }); + handlePostRequest(req, res); return; } @@ -251,6 +288,14 @@ function requestHandler(req, res) { } else { res.end(getStateBasedIndexPage()); } + } else if (urlobj.pathname === '/wifiNetworks') { + if (fs.existsSync(NETWORKS_FILE)) { + res.setHeader('content-type', getContentType(NETWORKS_FILE)); + res.end(fs.readFileSync(NETWORKS_FILE, {encoding: 'utf8'})); + } else { + res.statusCode = 404; + res.end("Please try again later."); + } } else { // for files like .css and images. if (!fs.existsSync(site + urlobj.pathname)) { pageNotFound(res);